summaryrefslogtreecommitdiffstats
path: root/private/posix
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/posix
downloadNT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip
Diffstat (limited to 'private/posix')
-rw-r--r--private/posix/ballot/1003.3276
-rw-r--r--private/posix/ballot/1003.311161
-rw-r--r--private/posix/ballot/1003.res185
-rw-r--r--private/posix/ballot/1003.rsp108
-rw-r--r--private/posix/build.psx74
-rw-r--r--private/posix/client/alpha/psxthunk.s149
-rw-r--r--private/posix/client/alpha/sources1
-rw-r--r--private/posix/client/buildtst.cmd3
-rw-r--r--private/posix/client/coninit.c125
-rw-r--r--private/posix/client/conreqst.c56
-rw-r--r--private/posix/client/crtsup.c233
-rw-r--r--private/posix/client/dllext.c88
-rw-r--r--private/posix/client/dllfile.c1011
-rw-r--r--private/posix/client/dllinit.c377
-rw-r--r--private/posix/client/dllio.c694
-rw-r--r--private/posix/client/dllname.c310
-rw-r--r--private/posix/client/dllproc.c1251
-rw-r--r--private/posix/client/dllreg.c173
-rw-r--r--private/posix/client/dllsig.c438
-rw-r--r--private/posix/client/dlltc.c293
-rw-r--r--private/posix/client/dlltimer.c243
-rw-r--r--private/posix/client/i386/psxthunk.asm60
-rw-r--r--private/posix/client/i386/sources1
-rw-r--r--private/posix/client/makefile6
-rw-r--r--private/posix/client/makefile.inc2
-rw-r--r--private/posix/client/mips/psxthunk.s147
-rw-r--r--private/posix/client/mips/sources1
-rw-r--r--private/posix/client/ppc/psxthunk.s185
-rw-r--r--private/posix/client/ppc/sources1
-rw-r--r--private/posix/client/psxdll.h176
-rw-r--r--private/posix/client/psxdll.rc11
-rw-r--r--private/posix/client/psxdll.src151
-rw-r--r--private/posix/client/sources64
-rw-r--r--private/posix/client/stubs.c195
-rw-r--r--private/posix/client/sysdb.c304
-rw-r--r--private/posix/client/tst/tstdir.c308
-rw-r--r--private/posix/client/tst/tstexec.c68
-rw-r--r--private/posix/client/tst/tstfile.c252
-rw-r--r--private/posix/client/tst/tstfork.c598
-rw-r--r--private/posix/client/tst/tstheap.c111
-rw-r--r--private/posix/client/tst/tsthello.c38
-rw-r--r--private/posix/client/tst/tsthw.c20
-rw-r--r--private/posix/client/tst/tstjc.c367
-rw-r--r--private/posix/client/tst/tstloop.c83
-rw-r--r--private/posix/client/tst/tstmd.c54
-rw-r--r--private/posix/client/tst/tstmisc.c242
-rw-r--r--private/posix/client/tst/tstncall.c55
-rw-r--r--private/posix/client/tst/tstnpipe.c420
-rw-r--r--private/posix/client/tst/tstrmdir.c91
-rw-r--r--private/posix/client/tst/tstsid.c284
-rw-r--r--private/posix/client/tst/tstsig.c120
-rw-r--r--private/posix/client/tst/tstsum.c138
-rw-r--r--private/posix/client/tst/tsttime.c40
-rw-r--r--private/posix/client/tst/tsttmp.h5
-rw-r--r--private/posix/client/tst/tstumask.c68
-rw-r--r--private/posix/dirs28
-rw-r--r--private/posix/filereqs.psx104
-rw-r--r--private/posix/inc/depend.mak19
-rw-r--r--private/posix/inc/psxalpha.h27
-rw-r--r--private/posix/inc/psxi386.h27
-rw-r--r--private/posix/inc/psxi860.h27
-rw-r--r--private/posix/inc/psxmips.h27
-rw-r--r--private/posix/inc/psxmsg.h1000
-rw-r--r--private/posix/inc/psxppc.h27
-rw-r--r--private/posix/inc/psxsrv.h1876
-rw-r--r--private/posix/inc/sesport.h270
-rw-r--r--private/posix/pcd.docbin0 -> 92672 bytes
-rw-r--r--private/posix/programs/bsdlib/bcopy.c9
-rw-r--r--private/posix/programs/bsdlib/bzero.c10
-rw-r--r--private/posix/programs/bsdlib/for2bak.c13
-rw-r--r--private/posix/programs/bsdlib/fts.c870
-rw-r--r--private/posix/programs/bsdlib/getenv.c91
-rw-r--r--private/posix/programs/bsdlib/getopt.c123
-rw-r--r--private/posix/programs/bsdlib/getwd.c26
-rw-r--r--private/posix/programs/bsdlib/index.c13
-rw-r--r--private/posix/programs/bsdlib/lstat.c11
-rw-r--r--private/posix/programs/bsdlib/makefile6
-rw-r--r--private/posix/programs/bsdlib/mknod.c23
-rw-r--r--private/posix/programs/bsdlib/posixdbg.c2258
-rw-r--r--private/posix/programs/bsdlib/putenv.c65
-rw-r--r--private/posix/programs/bsdlib/pwcache.c117
-rw-r--r--private/posix/programs/bsdlib/rindex.c10
-rw-r--r--private/posix/programs/bsdlib/scandir.c153
-rw-r--r--private/posix/programs/bsdlib/seekdir.c12
-rw-r--r--private/posix/programs/bsdlib/setenv.c122
-rw-r--r--private/posix/programs/bsdlib/setmode.c461
-rw-r--r--private/posix/programs/bsdlib/snprintf.c81
-rw-r--r--private/posix/programs/bsdlib/sources43
-rw-r--r--private/posix/programs/bsdlib/strmode.c164
-rw-r--r--private/posix/programs/bsdlib/stubs.c51
-rw-r--r--private/posix/programs/bsdlib/telldir.c10
-rw-r--r--private/posix/programs/bsdlib/utimes.c39
-rw-r--r--private/posix/programs/bsdlib/vfork.c6
-rw-r--r--private/posix/programs/dirs25
-rw-r--r--private/posix/programs/hello/makefile6
-rw-r--r--private/posix/programs/hello/psxhello.c19
-rw-r--r--private/posix/programs/hello/sources17
-rw-r--r--private/posix/programs/inc/bsd/ar.h62
-rw-r--r--private/posix/programs/inc/bsd/fts.h121
-rw-r--r--private/posix/programs/inc/bsd/ranlib.h50
-rw-r--r--private/posix/programs/inc/bsd/sys/cdefs.h78
-rw-r--r--private/posix/programs/inc/bsd/sys/mount.h298
-rw-r--r--private/posix/programs/inc/bsd/tzfile.h151
-rw-r--r--private/posix/programs/inc/bsd/utmp.h60
-rw-r--r--private/posix/programs/inc/df/bsdlib.h28
-rw-r--r--private/posix/programs/inc/df/misc.h99
-rw-r--r--private/posix/programs/inc/local.h86
-rw-r--r--private/posix/programs/inc/pwd.h85
-rw-r--r--private/posix/programs/pax/append.c97
-rw-r--r--private/posix/programs/pax/buffer.c949
-rw-r--r--private/posix/programs/pax/config.h193
-rw-r--r--private/posix/programs/pax/cpio.c227
-rw-r--r--private/posix/programs/pax/create.c426
-rw-r--r--private/posix/programs/pax/extract.c650
-rw-r--r--private/posix/programs/pax/fileio.c498
-rw-r--r--private/posix/programs/pax/func.h199
-rw-r--r--private/posix/programs/pax/getopt.c108
-rw-r--r--private/posix/programs/pax/link.c335
-rw-r--r--private/posix/programs/pax/list.c714
-rw-r--r--private/posix/programs/pax/makefile6
-rw-r--r--private/posix/programs/pax/mem.c144
-rw-r--r--private/posix/programs/pax/namelist.c553
-rw-r--r--private/posix/programs/pax/names.c268
-rw-r--r--private/posix/programs/pax/pass.c183
-rw-r--r--private/posix/programs/pax/pathname.c252
-rw-r--r--private/posix/programs/pax/pax.c557
-rw-r--r--private/posix/programs/pax/pax.h408
-rw-r--r--private/posix/programs/pax/pax.rc12
-rw-r--r--private/posix/programs/pax/paxdir.c713
-rw-r--r--private/posix/programs/pax/paxdir.h68
-rw-r--r--private/posix/programs/pax/port.c205
-rw-r--r--private/posix/programs/pax/port.h93
-rw-r--r--private/posix/programs/pax/regexp.c1511
-rw-r--r--private/posix/programs/pax/regexp.h41
-rw-r--r--private/posix/programs/pax/replace.c320
-rw-r--r--private/posix/programs/pax/sources43
-rw-r--r--private/posix/programs/pax/stubs.c8
-rw-r--r--private/posix/programs/pax/tar.c350
-rw-r--r--private/posix/programs/pax/ttyio.c289
-rw-r--r--private/posix/programs/pax/warn.c266
-rw-r--r--private/posix/programs/pax/wildmat.c203
-rw-r--r--private/posix/programs/psxarc/archive.c118
-rw-r--r--private/posix/programs/psxarc/buf.c177
-rw-r--r--private/posix/programs/psxarc/buf.h27
-rw-r--r--private/posix/programs/psxarc/cpio.c318
-rw-r--r--private/posix/programs/psxarc/cpio.h50
-rw-r--r--private/posix/programs/psxarc/getopt.c78
-rw-r--r--private/posix/programs/psxarc/getopt.h13
-rw-r--r--private/posix/programs/psxarc/links.c64
-rw-r--r--private/posix/programs/psxarc/links.h39
-rw-r--r--private/posix/programs/psxarc/makefile6
-rw-r--r--private/posix/programs/psxarc/psxarc.c124
-rw-r--r--private/posix/programs/psxarc/psxarc.h16
-rw-r--r--private/posix/programs/psxarc/sources29
-rw-r--r--private/posix/programs/psxarc/tar.c457
-rw-r--r--private/posix/programs/psxarc/tarhead.h28
-rw-r--r--private/posix/programs/psxses/ansiio.h89
-rw-r--r--private/posix/programs/psxses/conreqst.c159
-rw-r--r--private/posix/programs/psxses/error.c35
-rw-r--r--private/posix/programs/psxses/error.h3
-rw-r--r--private/posix/programs/psxses/errors.mc20
-rw-r--r--private/posix/programs/psxses/kbdutil.c71
-rw-r--r--private/posix/programs/psxses/makefile6
-rw-r--r--private/posix/programs/psxses/makefile.inc2
-rw-r--r--private/posix/programs/psxses/ntinitss.c1125
-rw-r--r--private/posix/programs/psxses/ntreqst.c150
-rw-r--r--private/posix/programs/psxses/posix.c314
-rw-r--r--private/posix/programs/psxses/posix.rc19
-rw-r--r--private/posix/programs/psxses/posixres.h4
-rw-r--r--private/posix/programs/psxses/psxses.h114
-rw-r--r--private/posix/programs/psxses/sources59
-rw-r--r--private/posix/programs/psxses/tcreqst.c86
-rw-r--r--private/posix/programs/psxses/terminp.c715
-rw-r--r--private/posix/programs/psxses/termoutp.c837
-rw-r--r--private/posix/programs/psxses/tmreqst.c52
-rw-r--r--private/posix/programs/psxses/util.c63
-rw-r--r--private/posix/programs/psxses/util.h31
-rw-r--r--private/posix/psxss/acledit.c523
-rw-r--r--private/posix/psxss/apiinit.c152
-rw-r--r--private/posix/psxss/apilistn.c192
-rw-r--r--private/posix/psxss/apireqst.c508
-rw-r--r--private/posix/psxss/concreat.c276
-rw-r--r--private/posix/psxss/coninit.c123
-rw-r--r--private/posix/psxss/conio.c502
-rw-r--r--private/posix/psxss/consignl.c86
-rw-r--r--private/posix/psxss/conthrds.c201
-rw-r--r--private/posix/psxss/fdio.c810
-rw-r--r--private/posix/psxss/fileio.c820
-rw-r--r--private/posix/psxss/flocks.c954
-rw-r--r--private/posix/psxss/lpipeio.c1322
-rw-r--r--private/posix/psxss/makefile6
-rw-r--r--private/posix/psxss/nullio.c421
-rw-r--r--private/posix/psxss/procblk.c125
-rw-r--r--private/posix/psxss/process.c1914
-rw-r--r--private/posix/psxss/psxss.c112
-rw-r--r--private/posix/psxss/psxss.rc12
-rw-r--r--private/posix/psxss/psxsup.c850
-rw-r--r--private/posix/psxss/sbapi.c83
-rw-r--r--private/posix/psxss/sbinit.c82
-rw-r--r--private/posix/psxss/sbreqst.c143
-rw-r--r--private/posix/psxss/session.c115
-rw-r--r--private/posix/psxss/sigapi.c429
-rw-r--r--private/posix/psxss/sigsup.c493
-rw-r--r--private/posix/psxss/sources80
-rw-r--r--private/posix/psxss/srvdebug.c43
-rw-r--r--private/posix/psxss/srvfile.c1152
-rw-r--r--private/posix/psxss/srvhandl.c3303
-rw-r--r--private/posix/psxss/srvinit.c262
-rw-r--r--private/posix/psxss/srvtask.c1948
-rw-r--r--private/posix/psxss/srvtc.c200
-rw-r--r--private/posix/psxss/stub.c100
-rw-r--r--private/posix/psxss/sysdb.c1258
-rw-r--r--private/posix/psxss/timer.c322
-rw-r--r--private/posix/rtl/makefile6
-rw-r--r--private/posix/rtl/sources39
-rw-r--r--private/posix/rtl/startup.c68
-rw-r--r--private/posix/rtl/stubs.c36
-rw-r--r--private/posix/status.psx306
-rw-r--r--private/posix/tests.psx67
219 files changed, 57150 insertions, 0 deletions
diff --git a/private/posix/ballot/1003.3 b/private/posix/ballot/1003.3
new file mode 100644
index 000000000..4b0f30b30
--- /dev/null
+++ b/private/posix/ballot/1003.3
@@ -0,0 +1,276 @@
+
+
+OBJECTION Part I, Section 4.0, Page 13, Lines 84-86
+
+
+ The definition of PCTS shall be a conforming application
+ is too vauge.
+
+
+ REQUIRED ACTION: Add the following to this paragraph:
+ The PCTS shall be a "Strictly Conforming POSIX Application".
+
+ If the PCTS requires a certain environment available on the
+ target system, it shall be possible to create that environment
+ using a Strictly Conforming POSIX.1 Application. If such an
+ application is required, then the PCTS shall include such an
+ application.
+
+ Environment includes the presence of a named file protected in
+ a certain way, the presence and contents of any directory or file
+ hierarchy...
+
+--- NEW PAGE ---
+
+OBJECTION Part I, Section 5.0, Page 14, Line 107
+
+
+ Specification of software requirements is too vauge.
+
+
+ REQUIRED ACTION: Add the following to this line:
+ The specification of software requirements necessary to execute
+ the PCTS shall be confined to an environment that may be created
+ using a PCTS supplied Strictly Conforming POSIX.1 Application.
+
+
+------------------------------------------------------------------------------
+
+OBJECTION Part I, Section 5.0, Page 14, Lines 109-110
+
+
+ Description of procedure to transfer PCTS to target system is
+ too open ended.
+
+
+ REQUIRED ACTION: Add the following to this line:
+ If the PCTS requires a transfer to the target system. The
+ procedure used to transfer the PCTS shall be a Strictly
+ Conforming POSIX.1 Application supplied with the PCTS. All
+ PCTS's shall be transferable from the development system to the
+ target system. All PCTS's must contain this documentation and
+ the corresponding Strictly Conforming POSIX.1 Application
+ necessary to transfer the PCTS.
+
+
+--- NEW PAGE ---
+
+
+------------------------------------------------------------------------------
+
+OBJECTION Part I, Section 10.0, Page 22, Lines 301-303
+
+
+ A result code of FAIL should not be possible for an extended
+ assertion.
+
+
+ REQUIRED ACTION: Change the table as follows:
+ For required feature extended assertions, permissable result
+ codes shall be limited to PASS or UNTESTED.
+
+ For optional feature extended assertions, permissable result
+ codes shall be limited to PASS, UNSUPPORTED, or UNTESTED.
+
+ It shall not be possible to FAIL an extended assertion. The
+ POSIX.3 Draft has made a very clear distinction between base
+ assertions, and extended assertions. Base assertions shall be
+ tested and are infact testable. Extended assertions which do
+ not require a test should not effect an implementations ability
+ to acheive complience.
+
+ Since assertions are classified as extended assertions when the
+ assertion is not testable, failing such a test should not be
+ possible and should not effect an implementations ability to
+ acheive compliance.
+
+
+--- NEW PAGE ---
+
+------------------------------------------------------------------------------
+
+OBJECTION Part I, Section 13.0, Page 26, Lines 365-367
+
+
+ The result code of an extended assertion should have no
+ bearing on an implementations designation as compliant.
+
+
+ REQUIRED ACTION: Remove lines 365-368 from this document.
+ The POSIX.3 Draft has made a very clear distinction between base
+ assertions, and extended assertions. Base assertions shall be
+ tested and are infact testable. Extended assertions which do
+ not require a test should not effect an implementations ability
+ to acheive complience.
+
+ Since assertions are classified as extended assertions when the
+ assertion is not testable, failing such a test should not be
+ possible and should not effect an implementations ability to
+ acheive compliance.
+
+
+--- NEW PAGE ---
+
+OBJECTION Part I, Section 8.0, Page 19, Line 215
+
+
+ Specification of setup procedures and effort is too vauge.
+
+
+ REQUIRED ACTION: Add the following to this line:
+ If testing of the assertion requires setup procedures or effort
+ on the part of the testor that is not possible to acheive using
+ a Strictly Conforming POSIX.1 Application, then the assertion
+ shall not be considered an assertion, base assertion, or an
+ extended assertion. If a PCTS attempts to test such an
+ assertion, the results of the test shall not affect the
+ implementations conformance/compliance and no record of the test
+ shall appear in any PCTS output.
+
+
+--- NEW SECTION ---
+
+OBJECTION Part II, Section 1.2.1, Page 2,3, Lines 27-47
+
+
+ This section places an unacceptable burden on an implementation.
+
+
+ REQUIRED ACTION: Add the following to this section:
+ If testing an assertion requires that an operational environment
+ be created which requires the target system to provide
+ functionality which is beyond the scope of POSIX.1, that
+ assertion shall be declared a "non-assertion" which by
+ definition is UNTESTABLE and can not be used to determine
+ an implementations compliance.
+
+ Under no circumstances should it be permissible to require a
+ target system which is claiming POSIX.1 complience to provide
+ additional features outside the scope of POSIX.1. To do this
+ would place an unacceptable burden on those implementations that
+ are hosted, or are designed in clean room conditions in
+ accordance with the documented POSIX.1 standard.
+
+ It shall be possible to fully test an implementation that only
+ provides those features described in POSIX.1. Such a system
+ shall be a compliant system.
+
+ If an implementation choses to support additional features which
+ are outside the scope of POSIX.1, but if implemented would allow
+ the testing of a previously UNTESTABLE assertion, then the
+ assertion may be tested as an extended assertion. The results
+ of any such assertion test shall have no impact on an
+ implementations ability to acheive compliance.
+
+ If testing of the assertion requires setup procedures or effort
+ on the part of the testor that is not possible to acheive using
+ a Strictly Conforming POSIX.1 Application, then the assertion
+ shall not be considered an assertion, base assertion, or an
+ extended assertion. If a PCTS attempts to test such an
+ assertion, the results of the test shall not affect the
+ implementations conformance/compliance and no record of the test
+ shall appear in any PCTS output.
+
+ The rationale used to arrive at the current draft of this
+ section is flawed. It does not consider hosted implementations
+ or implementations developed under clean room conditions. The
+ rationale assumes that PCTS's will be targeted at a limited set
+ of implementations where the required facilities could be
+ provided in a standard way. This assumes that all POSIX.1
+ implementations are acheived by having a vendor modify its
+ current UNIX product such that it supports the required POSIX.1
+ facilities. The target system support facility would be
+ provided by the underlying AT&T derived UNIX system. Not all
+ implementations of POSIX.1 are derived from AT&T source code, or
+ are acheived my modifying an existing UNIX system. Clean room
+ implementations of POSIX where the only documentation is the
+ POSIX.1 standard must be addressed. The only way to adequetly
+ address these systems is to allow an implementation that only
+ implements those facilities documented in POSIX.1 to be
+ considered as a POSIX.1 compliant system. It would be improper
+ for this document to demand that UNIX like support facilities
+ are needed in order to implement POSIX.1.
+
+-------------------------------------------------------------------------------
+
+OBJECTION Part II, Section 1.2.1.2, Page 3, Lines 45-47
+
+
+ Providing a "target system support facility" should not be a
+ requirement of a POSIX.1 implementation.
+
+
+ REQUIRED ACTION: Revise this section.
+ Any assertion that requires an implementation to provide a
+ "target system support facility" that is beyond the scope of
+ POSIX.1 shall be classified as an untestable assertion. The
+ outcome of a test on such an assertion shall have no bearing on
+ an implementations ability to acheive compliance, and shall not
+ appear in any output from a PCTS.
+
+ It shall be possible to fully test an implementation that only
+ provides those features described in POSIX.1. Such a system
+ shall be a compliant system.
+
+ Failure allow a system which provides only those facilities
+ described in POSIX.1 to be classified as a compliant system
+ is unacceptable. If additional facilities are required, then
+ the time should be taken to extend POSIX.1 to incorporate such
+ facilities.
+
+ There are many situations in hosted implementations, or in
+ implementations developed under "clean room" conditions where
+ the POSIX.1 documentation is the only documentation used to
+ design an implementation.
+
+ Approval of this section and section 1.9 would force many of
+ these implementations to choose a PCTS based on its test
+ coverage, and the demands that it placed on the target support
+ facility.
+
+ This document must allow for a PCTS that requires only those
+ facilities provided by POSIX.1.
+
+
+-------------------------------------------------------------------------------
+
+OBJECTION Part II, Section 1.9, Page 7,8, Lines 208-228
+
+
+ This section places an unacceptable burden on the target hardware.
+
+
+ REQUIRED ACTION: remove the closed-loop requirement.
+ It is unacceptable to require that a target system support
+ multiple terminal ports. It shall be possible to build a
+ POSIX.1 compliant system that has no asynchronous communication
+ ports. It shall be acceptable to implement a POSIX.1 compliant
+ system whose only "terminal like" input output capabilities are
+ implemented as a layer ontop of the keyboard/display hardware
+ commonly found on Workstations and PC's.
+
+
+--- NEW PAGE ---
+
+-------------------------------------------------------------------------------
+
+OBJECTION Part II, Section 7, Page 158-184, Lines 2-709
+
+
+ Terminals, and are not well defined.
+
+
+ REQUIRED ACTION: Make all assertions in this section optional,
+ or extended assertions.
+
+ The note on line 6 of this section sums up the problems with the
+ section. Major portions of this section are only valid when a
+ terminal is an "asynchronous serial terminal".
+
+ There are systems that whose I/O system consists of a disk,
+ video controller, and keyboard input. It shall be possible to
+ to classify such a system as POSIX.1 compliant.
+
+ An implementation for such a target system would have to fail
+ all calls to isatty() since its I/O system is not capable of
+ supporting all assertions specified in this section.
diff --git a/private/posix/ballot/1003.311 b/private/posix/ballot/1003.311
new file mode 100644
index 000000000..7c0106f70
--- /dev/null
+++ b/private/posix/ballot/1003.311
@@ -0,0 +1,161 @@
+IEEE 1003.3 Recirculation Ballot
+
+
+March 20, 1990
+
+
+To: Computer Society Secritariat
+ iEEE Standards Office
+ ATTN: P1003.1a Ballot (Bob Pritchard)
+ 445 Hoes Lane
+ Piscataway, NJ 08855-133
+
+
+I DO NOT approve as a full use standard 1003.3/D11.
+
+
+------------------------------------------------------------------------------
+Part I Section(s) 7-8 Page(s) 24-26 Line(s) 447-501
+Balloter: Gregory W. Goddard (206) 867-3629 ...!uunet!microsoft!markl
+Identification: XXXX
+Position on Submittal: OBJECTION
+
+ Test result code of UNRESOLVED is too restrictive. Lines 463-464
+ require that UNRESOLVED test result codes shall resolve to one of
+ the other test codes before a statement of compliance is made.
+
+ For a (B) assertion, this means that UNRESOLVED must go to PASS, or
+ UNTESTED. Since UNTESTED and UNRESOLVED contridict each other,
+ UNRESOLVEs must resolve to PASS. This is not acceptible since a
+ test can result in UNRESOLVE because "Setup for the assertion test
+ failed".
+
+ If a PCTS requires target system support facilities to setup for a
+ test, and the facilities do not exist, then using the above
+ rational, the assertion test can go to UNRESOLVE. In this
+ situation, the only way to get a statement of complience is to
+ implement system support facilities, retest, and get a PASS. This
+ is to restrictive.
+
+Required Action:
+ Add a test result code of UNTESTABLE with the following definition:
+
+ UNTESTABLE - An assertion test resulted in an UNRESOLVED test result
+ code. After careful examinition, it is discovered that the test
+ can not be resolved to PASS because the system being tested
+ lacks optional target system support facilities that would
+ be required to setup for the assertion test.
+
+ In the chart on page 26, add the UNTESTABLE test result code to all
+ cells.
+
+------------------------------------------------------------------------------
+
+------------------------------------------------------------------------------
+Part II Section(s) 3.1.2.2 Page(s) 37 Line(s) 258-262
+Balloter: Gregory W. Goddard (206) 867-3629 ...!uunet!microsoft!markl
+Identification: XXXX
+Position on Submittal: OBJECTION
+
+ Assertions 30 and 31 are classified incorrectly. Since there is no
+ portable way of creating an executable file with either the S_ISUID,
+ or S_ISGID mode bits set, defining these as (A) assertions is
+ inappropriate.
+
+Required Action:
+ Change assertions 30 and 31 to (B) or (D) assertions.
+
+------------------------------------------------------------------------------
+
+------------------------------------------------------------------------------
+Part II Section(s) 4.2.3.2-4.2.3.4 Page(s) 85-86 Line(s) 214-240
+Balloter: Gregory W. Goddard (206) 867-3629 ...!uunet!microsoft!markl
+Identification: XXXX
+Position on Submittal: OBJECTION
+
+ Assertions 3, 4, 6 are classified incorrectly. Since there is no
+ portable way of modifying a process' list of supplementary group
+ ID's, testing the information returned by this call is questionable
+ if _SC_NGROUPS_MAX is greater than zero. Since there is no portable
+ way to set the number of supplementary group id's in a process,
+ verifying that the information returned by getgroups() is correct
+ can not be done portably.
+
+Required Action:
+ Change assertions 3, 4, and 6 to (B) or (D) assertions.
+
+------------------------------------------------------------------------------
+------------------------------------------------------------------------------
+Part II Section(s) 4.7.1.2 Page(s) 101 Line(s) 621-624
+Balloter: Gregory W. Goddard (206) 867-3629 ...!uunet!microsoft!markl
+Identification: XXXX
+Position on Submittal: OBJECTION
+
+ Assertions 3 and 4 are classified incorrectly. Since there is no
+ portable way of establishing the controlling terminal for a process,
+ there is no way to verify the correctness of this function.
+
+Required Action:
+ Change assertions 3 and 4 to (B) or (D) assertions.
+
+------------------------------------------------------------------------------
+
+------------------------------------------------------------------------------
+Part II Section(s) 5.1.2.2 Page(s) 110 Line(s) 104-105
+Balloter: Gregory W. Goddard (206) 867-3629 ...!uunet!microsoft!markl
+Identification: XXXX
+Position on Submittal: OBJECTION
+
+ Assertion 8 is classified incorrectly. Since there is no portable
+ way of causing the underlying directory to be read, there is no way
+ to test when the st_atime field of the directory should be marked
+ for update.
+
+Required Action:
+ Change assertion 8 (B) or (D) assertions.
+
+------------------------------------------------------------------------------
+------------------------------------------------------------------------------
+Part II Section(s) 5.4.1.4 Page(s) 134 Line(s) 765-768
+Balloter: Gregory W. Goddard (206) 867-3629 ...!uunet!microsoft!markl
+Identification: XXXX
+Position on Submittal: OBJECTION
+
+ Assertion 14 is based on an incorrect assumption. This assertion is
+ based on the assumption that creating a directory causes the link
+ count of the parent directory to be incremented. This is not always
+ the case, and is certainly not required POSIX.1 functionality. The
+ link count bias occurs in UNIX systems due to the ".." entry created
+ in the new directory. Implementations that support the ".."
+ concept, but that do not actually create an entry for ".." do not
+ cause the link count of the parent directory to be incremented. The
+ description of readdir() allows for directories that contain no
+ entry for "..", and therefore do not cause the link count in the
+ parent directory to be incremented.
+
+Required Action:
+ Change assertion 14 to 14(C) and make it read as follows:
+
+ If {_POSIX_LINK_MAX} <= {LINK_MAX} <= {PCTS_LINK_MAX} and if
+ creating a directory causes the link count of the directory in which
+ path1 is to be created to be incremented:
+ When {LINK_MAX} links to the directory in which path1 is to be
+ created already exist, then a call to mkdir(path1,mode) returns
+ a value of ((int)-1), sets errno to [EMLINK], and no directory
+ is created.
+
+------------------------------------------------------------------------------
+------------------------------------------------------------------------------
+Part II Section(s) 5.6.1.1 Page(s) 149 Line(s) 1232-1237
+Balloter: Gregory W. Goddard (206) 867-3629 ...!uunet!microsoft!markl
+Identification: XXXX
+Position on Submittal: OBJECTION
+
+ Assertions 4 and 5 are classified incorrectly. Since there is no
+ portable way of creating a character special file or a block special
+ file, there is no portable way to test these assertions.
+
+Required Action:
+ Change assertions 4 and 5 to (B) or (D) assertions.
+
+------------------------------------------------------------------------------
diff --git a/private/posix/ballot/1003.res b/private/posix/ballot/1003.res
new file mode 100644
index 000000000..2cf474aa2
--- /dev/null
+++ b/private/posix/ballot/1003.res
@@ -0,0 +1,185 @@
+
+| >From uunet!swe.ncsl.nist.gov!cincotta Fri Dec 7 11:23:34 1990
+| Received: from SWE.NCSL.NIST.GOV by uunet.UU.NET (5.61/1.14) with SMTP
+| id AA03137; Fri, 7 Dec 90 13:13:48 -0500
+| Received: by swe.ncsl.nist.gov (4.1/NIST(rbj/dougm))
+| id AA04973; Fri, 7 Dec 90 13:15:22 EST
+| Date: Fri, 7 Dec 90 13:15:22 EST
+| >From: Tony Cincotta <uunet!swe.ncsl.nist.gov!cincotta>
+| Organization: National Institute of Standards and Technology (NIST)
+| Sub-Organization: National Computer Systems Laboratory
+| Message-Id: <9012071815.AA04973@swe.ncsl.nist.gov>
+| To: microsoft!markl
+| Subject: Your Draft 11.0 Part 2, P1003.3 ballot resolutions
+|
+|
+| Included in this message are the resolutions of Part 2 of your P1003.3
+| Ballot of Draft 11.0 (January 24, 1990). Please acknowledge receipt
+| of this message.
+|
+| ALL ballot items marked ACCEPT_WITH_MODS or REJECT will be considered
+| as ACCEPTED BY YOU THE BALLOTTER. If you initially objected to an item
+| and do not accept the resolution of that item, or have not been given
+| enough information on the resolution of the item you may take one of the
+| following possible actions:
+| 1) REJECT the resolution with no additional comments. We will then
+| publish the item as an Unresolved Objection in the next
+| recirculation ballot. Please provide the "Identification number"
+| of these items.
+| 2) Request additional information on the actual changes made to the
+| draft.
+| 3) REJECT the resolution and provide additional comments for
+| consideration. This item will then be discussed by the TR
+| committee and if your ballot item is not completely accepted
+| by the TR committee, a TR will get in touch with you.
+| 4) Wait for the next draft.
+|
+| Please mail all responses to this message to cincotta@swe.ncsl.nist.gov.
+| I will forward the info to the responsible individual. Mail received
+| after January 2, 1991 may not be considered for inclusion in the next
+| P1003.3.1 recirculation.
+|
+|
+| ------------------------------------------------------------
+| Part 2 Section(s) 3.1.2.2 Page(s) 37 Line(s) 258-262
+| Balloter: Gregory W. Goddard (206) 867-3629 ...!uunet!microsoft!markl
+| Identification: 0121 Position on Submittal: OBJECTION
+|
+| Assertions 30 and 31 are classified incorrectly. Since there is no
+| portable way of creating an executable file with either the S_ISUID,
+| or S_ISGID mode bits set, defining these as (A) assertions is
+| inappropriate.
+|
+| Required Action:
+| Change assertions 30 and 31 to (B) or (D) assertions.
+|
+| RESOLUTION:ACCEPT_WITH_MODS:
+| Prefix assertion 30 with "If the implementation supports a method
+| for setting the S_ISUID mode bit:
+| Change assertion type to C.
+|
+| Prefix assertion 31 with "If the implementation supports a method
+| for setting the S_ISGID mode bit:
+| Change assertion type to C.
+|
+| ------------------------------------------------------------
+| Part 2 Section(s) 4.2.3.2-4.2.3.4 Page(s) 85-86 Line(s) 214-240
+| Balloter: Gregory W. Goddard (206) 867-3629 ...!uunet!microsoft!markl
+| Identification: 0122 Position on Submittal: OBJECTION
+|
+| Assertions 3, 4, 6 are classified incorrectly. Since there is no
+| portable way of modifying a process' list of supplementary group
+| ID's, testing the information returned by this call is questionable
+| if _SC_NGROUPS_MAX is greater than zero. Since there is no portable
+| way to set the number of supplementary group id's in a process,
+| verifying that the information returned by getgroups() is correct
+| can not be done portably.
+|
+| Required Action:
+| Change assertions 3, 4, and 6 to (B) or (D) assertions.
+|
+| RESOLUTION:DISCUSSION:
+| Change to C type assertions with the condition:
+|
+| "If the implementation provides a mechanism to create a list of
+| supplementary Ids for a process"
+|
+| TR3:
+| I see no reason for changing this text.
+|
+| POSIX.1 defines NGROUPS_MAX as an option. POSIX.1 does not define
+| the method of implementing NGROUPS_MAX. Therefore, according to
+| our definition for "conditional features" the method of implementing
+| NGROUPS_MAX is not a conditional feature.
+|
+| This is a PCTS installation procedure.
+|
+| RESOLUTION:REJECT:
+|
+| ------------------------------------------------------------
+| Part 2 Section(s) 4.7.1.2 Page(s) 101 Line(s) 621-624
+| Balloter: Gregory W. Goddard (206) 867-3629 ...!uunet!microsoft!markl
+| Identification: 0123 Position on Submittal: OBJECTION
+|
+| Assertions 3 and 4 are classified incorrectly. Since there is no
+| portable way of establishing the controlling terminal for a process,
+| there is no way to verify the correctness of this function.
+|
+| Required Action:
+| Change assertions 3 and 4 to (B) or (D) assertions.
+|
+| RESOLUTION:DISCUSSION:
+| TR1:
+| Change to C type assertions with the condition "If the implementation
+| provides a method for allocating a controling terminal:"
+|
+| TR3:
+| The process should already have a controlling terminal. The PCTS doesn't
+| have to establish a process with a different controlling
+| terminal to check these assertions.
+|
+| RESOLUTION:REJECT:
+|
+| ------------------------------------------------------------
+| Part 2 Section(s) 5.1.2.2 Page(s) 110 Line(s) 104-105
+| Balloter: Gregory W. Goddard (206) 867-3629 ...!uunet!microsoft!markl
+| Identification: 0124 Position on Submittal: OBJECTION
+|
+| Assertion 8 is classified incorrectly. Since there is no portable
+| way of causing the underlying directory to be read, there is no way
+| to test when the st_atime field of the directory should be marked
+| for update.
+|
+| Required Action:
+| Change assertion 8 (B) or (D) assertions.
+|
+| RESOLUTION:REJECT:
+| It is at least known that a call to opendir() followed by a call
+| to readdir() will cause the underlying directory to be read.
+| ------------------------------------------------------------
+| Part 2 Section(s) 5.4.1.4 Page(s) 134 Line(s) 765-768
+| Balloter: Gregory W. Goddard (206) 867-3629 ...!uunet!microsoft!markl
+| Identification: 0125 Position on Submittal: OBJECTION
+|
+| Assertion 14 is based on an incorrect assumption. This assertion is
+| based on the assumption that creating a directory causes the link
+| count of the parent directory to be incremented. This is not always
+| the case, and is certainly not required POSIX.1 functionality. The
+| link count bias occurs in UNIX systems due to the ".." entry created
+| in the new directory. Implementations that support the ".."
+| concept, but that do not actually create an entry for ".." do not
+| cause the link count of the parent directory to be incremented. The
+| description of readdir() allows for directories that contain no
+| entry for "..", and therefore do not cause the link count in the
+| parent directory to be incremented.
+|
+| Required Action:
+| Change assertion 14 to 14(C) and make it read as follows:
+|
+| If {_POSIX_LINK_MAX} <= {LINK_MAX} <= {PCTS_LINK_MAX} and if
+| creating a directory causes the link count of the directory in which
+| path1 is to be created to be incremented:
+| When {LINK_MAX} links to the directory in which path1 is to be
+| created already exist, then a call to mkdir(path1,mode) returns
+| a value of ((int)-1), sets errno to [EMLINK], and no directory
+| is created.
+|
+| RESOLUTION:ACCEPT:
+| ------------------------------------------------------------
+| Part 2 Section(s) 5.6.1.1 Page(s) 149 Line(s) 1232-1237
+| Balloter: Gregory W. Goddard (206) 867-3629 ...!uunet!microsoft!markl
+| Identification: 0126 Position on Submittal: OBJECTION
+|
+| Assertions 4 and 5 are classified incorrectly. Since there is no
+| portable way of creating a character special file or a block special
+| file, there is no portable way to test these assertions.
+|
+| Required Action:
+| Change assertions 4 and 5 to (B) or (D) assertions.
+|
+| RESOLUTION:REJECT:
+| It is inconceivable that a POSIX.a conforming system does not have
+| a character special file and a block special file. There is no
+| requirement for the PCTS to create these only for the PCTS to
+| know the address of them.
+|
diff --git a/private/posix/ballot/1003.rsp b/private/posix/ballot/1003.rsp
new file mode 100644
index 000000000..51415794e
--- /dev/null
+++ b/private/posix/ballot/1003.rsp
@@ -0,0 +1,108 @@
+The following is the response to the ballot resolutions for P1003.3,
+Draft 11.0 Part 2. I apologize if the form of the response is
+incorrect. In brief, we reject without additional comment the 4 objections
+that were rejected in the resolution of the item.
+
+| ------------------------------------------------------------
+| Part 2 Section(s) 4.2.3.2-4.2.3.4 Page(s) 85-86 Line(s) 214-240
+| Balloter: Gregory W. Goddard (206) 867-3629 ...!uunet!microsoft!markl
+| Identification: 0122 Position on Submittal: OBJECTION
+|
+| Assertions 3, 4, 6 are classified incorrectly. Since there is no
+| portable way of modifying a process' list of supplementary group
+| ID's, testing the information returned by this call is questionable
+| if _SC_NGROUPS_MAX is greater than zero. Since there is no portable
+| way to set the number of supplementary group id's in a process,
+| verifying that the information returned by getgroups() is correct
+| can not be done portably.
+|
+| Required Action:
+| Change assertions 3, 4, and 6 to (B) or (D) assertions.
+|
+| RESOLUTION:DISCUSSION:
+| Change to C type assertions with the condition:
+|
+| "If the implementation provides a mechanism to create a list of
+| supplementary Ids for a process"
+|
+| TR3:
+| I see no reason for changing this text.
+|
+| POSIX.1 defines NGROUPS_MAX as an option. POSIX.1 does not define
+| the method of implementing NGROUPS_MAX. Therefore, according to
+| our definition for "conditional features" the method of implementing
+| NGROUPS_MAX is not a conditional feature.
+|
+| This is a PCTS installation procedure.
+|
+| RESOLUTION:REJECT:
+|
+| ** RESPONSE: REJECT
+|
+| ------------------------------------------------------------
+| Part 2 Section(s) 4.7.1.2 Page(s) 101 Line(s) 621-624
+| Balloter: Gregory W. Goddard (206) 867-3629 ...!uunet!microsoft!markl
+| Identification: 0123 Position on Submittal: OBJECTION
+|
+| Assertions 3 and 4 are classified incorrectly. Since there is no
+| portable way of establishing the controlling terminal for a process,
+| there is no way to verify the correctness of this function.
+|
+| Required Action:
+| Change assertions 3 and 4 to (B) or (D) assertions.
+|
+| RESOLUTION:DISCUSSION:
+| TR1:
+| Change to C type assertions with the condition "If the implementation
+| provides a method for allocating a controling terminal:"
+|
+| TR3:
+| The process should already have a controlling terminal. The PCTS doesn't
+| have to establish a process with a different controlling
+| terminal to check these assertions.
+|
+| RESOLUTION:REJECT:
+| 1) REJECT the resolution with no additional comments. We will then
+|
+| ** RESPONSE: REJECT
+|
+| ------------------------------------------------------------
+| Part 2 Section(s) 5.1.2.2 Page(s) 110 Line(s) 104-105
+| Balloter: Gregory W. Goddard (206) 867-3629 ...!uunet!microsoft!markl
+| Identification: 0124 Position on Submittal: OBJECTION
+|
+| Assertion 8 is classified incorrectly. Since there is no portable
+| way of causing the underlying directory to be read, there is no way
+| to test when the st_atime field of the directory should be marked
+| for update.
+|
+| Required Action:
+| Change assertion 8 (B) or (D) assertions.
+|
+| RESOLUTION:REJECT:
+| It is at least known that a call to opendir() followed by a call
+| to readdir() will cause the underlying directory to be read.
+|
+| ** RESPONSE: REJECT
+|
+| ------------------------------------------------------------
+| Part 2 Section(s) 5.6.1.1 Page(s) 149 Line(s) 1232-1237
+| Balloter: Gregory W. Goddard (206) 867-3629 ...!uunet!microsoft!markl
+| Identification: 0126 Position on Submittal: OBJECTION
+|
+| Assertions 4 and 5 are classified incorrectly. Since there is no
+| portable way of creating a character special file or a block special
+| file, there is no portable way to test these assertions.
+|
+| Required Action:
+| Change assertions 4 and 5 to (B) or (D) assertions.
+|
+| RESOLUTION:REJECT:
+| It is inconceivable that a POSIX.a conforming system does not have
+| a character special file and a block special file. There is no
+| requirement for the PCTS to create these only for the PCTS to
+| know the address of them.
+|
+| ** RESPONSE: REJECT
+|
+| ------------------------------------------------------------
diff --git a/private/posix/build.psx b/private/posix/build.psx
new file mode 100644
index 000000000..dd0337d62
--- /dev/null
+++ b/private/posix/build.psx
@@ -0,0 +1,74 @@
+
+Notes on Building and Running Posix (11-7-91)
+
+- enlist in the posix project (\nt\private\posix)
+
+ md \nt\private\posix
+ cd \nt\private\posix
+ enlist -s \\popcorn\razzle1 posix
+
+- build
+ cd \nt\private\posix
+ build
+ cd client
+ buildtst \\ builds tst*.exe - minimal internal tests
+
+NOTE rtl/stubs.c has stubs for CRT pieces that are missing from psxcrt.lib.
+These must be removed when the REAL crt library is complete.
+
+- copy executables and libraries to test machine
+
+ \nt\private\posix\psxss\obj\i386\psxss.exe --> nt\bin
+ \nt\private\posix\server\obj\i386\psxsrv.exe --> nt\bin
+ \nt\private\posix\programs\psxses\obj\i386\posix.exe --> nt\bin
+ \nt\private\posix\client\obj\i386\tst*.exe --> \nt\bin
+ \nt\public\sdk\lib\i386\psxdll.dll --> nt\dll
+
+- configure \nt\ntuser.cfg on test machine
+
+ add line in [Sm] section
+ SubSystem = Posix
+
+ change InitialCommand line to
+ InitialCommand = NOWINEXEC
+
+- boot using i386kd (kernel debugger running on os2 machine
+
+- at i386kd prompt after winlogon fails, type
+ ntsd -- winlogon
+
+- log in
+
+- start psxsrv and minimize the window
+
+- run the relevant test program
+
+TO RUN TESTS:
+
+The test machine must have the following:
+
+Hpfs file system on d:
+
+directory and files:
+ d:\psx
+ d:\psx\conffile contents unimportant
+ d:\psx\named.pip contents unimportant
+ d:\psx\tstf.one contents unimportant
+ d:\psx\tstf.two contents unimportant
+ d:\psx\tsthello.exe from nt\bin
+ d:\psx\out.dat contents unimportant
+ d:\psx\test
+ d:\psx\test\rmtst1
+ d:\psx\test\rmtst1\ab contents unimportant
+ d:\psx\test\rmtst2
+ d:\psx\test\rmtst2\.a contents unimportant
+ d:\psx\test\rmtst3
+ d:\psx\test\rmtst3\a contents unimportant
+ d:\psx\test\rmtst4
+ d:\psx\test\rmtst4\abcde contents unimportant
+ d:\psx\test\tstdirs
+ d:\psx\test\tstdirs\ some # of files and dirs - contents unimportant
+
+There are notes in the headers of each test file for what they require,
+but the above is fairly complete.
+
diff --git a/private/posix/client/alpha/psxthunk.s b/private/posix/client/alpha/psxthunk.s
new file mode 100644
index 000000000..ae7f9a41a
--- /dev/null
+++ b/private/posix/client/alpha/psxthunk.s
@@ -0,0 +1,149 @@
+// TITLE("POSIX Thunks")
+//++
+//
+// Copyright (c) 1991 Microsoft Corporation
+// Copyright (c) 1993 Digital Equipment Corporation
+//
+// Module Name:
+//
+// psxthunk.s
+//
+// Abstract:
+//
+// Author:
+//
+// Ellena Aycock-Wright (ellena) 11-Jan-1991
+//
+// Revision History:
+//
+// Thomas Van Baak (tvb) 11-Dec-1992
+//
+// Adapted for Alpha AXP.
+//
+//--
+
+#include "ksalpha.h"
+
+ SBTTL("Call Null Api")
+//++
+//
+// The following code is never executed. Its purpose is to support unwinding
+// through the call to the null API function. The instructions below are read
+// by virtual unwind to restore the context of the calling function.
+//
+//--
+
+ NESTED_ENTRY(PdxNullApiCall, ContextFrameLength, zero)
+
+ .set noreorder
+ .set noat
+ stq sp, CxIntSp(sp) // stack pointer
+ stq ra, CxIntRa(sp) // return address
+ stq ra, CxFir(sp) // continuation address
+ stq gp, CxIntGp(sp) // integer register gp
+
+ stq s0, CxIntS0(sp) // integer registers s0 - s5
+ stq s1, CxIntS1(sp) //
+ stq s2, CxIntS2(sp) //
+ stq s3, CxIntS3(sp) //
+ stq s4, CxIntS4(sp) //
+ stq s5, CxIntS5(sp) //
+ stq fp, CxIntFp(sp) // integer register fp
+
+ stt f2, CxFltF2(sp) // floating registers f2 - f9
+ stt f3, CxFltF3(sp) //
+ stt f4, CxFltF4(sp) //
+ stt f5, CxFltF5(sp) //
+ stt f6, CxFltF6(sp) //
+ stt f7, CxFltF7(sp) //
+ stt f8, CxFltF8(sp) //
+ stt f9, CxFltF9(sp) //
+ .set at
+ .set reorder
+
+ PROLOGUE_END
+
+ ALTERNATE_ENTRY(_PdxNullApiCaller)
+
+
+ mov sp, a0 // set address of context record
+ bsr ra, PdxNullApiCaller // call null api caller
+
+ .end PdxNullApiCaller
+
+ SBTTL("Call Signal Deliverer")
+//++
+//
+// The following code is never executed. Its purpose is to support unwinding
+// through the call to the signal deliverer. The instructions below are read
+// by virtual unwind to restore the context of the calling function.
+//
+//--
+
+ NESTED_ENTRY(PdxSignalDeliver, ContextFrameLength, zero)
+
+ .set noreorder
+ .set noat
+ stq sp, CxIntSp(sp) // stack pointer
+ stq ra, CxIntRa(sp) // return address
+ stq ra, CxFir(sp) // continuation address
+ stq gp, CxIntGp(sp) // integer register gp
+
+ stq s0, CxIntS0(sp) // integer registers s0 - s5
+ stq s1, CxIntS1(sp) //
+ stq s2, CxIntS2(sp) //
+ stq s3, CxIntS3(sp) //
+ stq s4, CxIntS4(sp) //
+ stq s5, CxIntS5(sp) //
+ stq fp, CxIntFp(sp) // integer register fp
+
+ stt f2, CxFltF2(sp) // floating registers f2 - f9
+ stt f3, CxFltF3(sp) //
+ stt f4, CxFltF4(sp) //
+ stt f5, CxFltF5(sp) //
+ stt f6, CxFltF6(sp) //
+ stt f7, CxFltF7(sp) //
+ stt f8, CxFltF8(sp) //
+ stt f9, CxFltF9(sp) //
+ .set at
+ .set reorder
+
+ PROLOGUE_END
+
+//++
+//
+// VOID
+// _PdxSignalDeliverer (
+// IN PCONTEXT Context,
+// IN sigset_t Mask,
+// IN int Signal,
+// IN _handler Handler
+// )
+//
+// Routine Description:
+//
+// The following routine provides linkage to POSIX client routines to
+// perform signal delivery.
+//
+// Arguments:
+//
+// s0 - s5 - Supply parameter values.
+//
+// sp - Supplies the address of a context record.
+//
+// Return Value:
+//
+// There is no return from these routines.
+//
+//--
+
+ ALTERNATE_ENTRY(_PdxSignalDeliverer)
+
+ mov sp, a0 // set address of context record
+ mov s1, a1 // set previous block mask
+ mov s2, a2 // set signal number
+ mov s3, a3 // set signal handler
+
+ bsr ra, PdxSignalDeliverer // deliver signal to POSIX client
+
+ .end _PsxSignalDeliverer
diff --git a/private/posix/client/alpha/sources b/private/posix/client/alpha/sources
new file mode 100644
index 000000000..92077dc40
--- /dev/null
+++ b/private/posix/client/alpha/sources
@@ -0,0 +1 @@
+ALPHA_SOURCES=psxthunk.asm
diff --git a/private/posix/client/buildtst.cmd b/private/posix/client/buildtst.cmd
new file mode 100644
index 000000000..ed85b1247
--- /dev/null
+++ b/private/posix/client/buildtst.cmd
@@ -0,0 +1,3 @@
+set NTDEBUG=ntsd
+set 386_OPTIMIZATION=/Od
+build tstdir tsthello tsthw tstmd tstnpipe tstsig tstfile tstmisc tstrmdir tstumask tstfork tstloop tstncall tstsid tstexec tstsum tstheap tsttime tstfp
diff --git a/private/posix/client/coninit.c b/private/posix/client/coninit.c
new file mode 100644
index 000000000..629dc348c
--- /dev/null
+++ b/private/posix/client/coninit.c
@@ -0,0 +1,125 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ coninit.c
+
+Abstract:
+
+ This module initialize the connection with the session console port
+
+Author:
+
+ Avi Nathan (avin) 23-Jul-1991
+
+Revision History:
+
+ Ellen Aycock-Wright (ellena) 15-Sept-1991 Modified for POSIX
+
+--*/
+
+#include <stdio.h>
+#include "psxdll.h"
+
+NTSTATUS
+PsxInitializeSessionPort(
+ IN ULONG UniqueId
+ )
+{
+ PSXSESCONNECTINFO ConnectionInfoIn;
+ ULONG ConnectionInfoInLength;
+
+ CHAR SessionName[PSX_SES_BASE_PORT_NAME_LENGTH];
+ STRING SessionPortName;
+ UNICODE_STRING SessionPortName_U;
+ STRING SessionDataName;
+ UNICODE_STRING SessionDataName_U;
+
+ NTSTATUS Status;
+ SECURITY_QUALITY_OF_SERVICE DynamicQos;
+ HANDLE SessionPortHandle;
+ HANDLE SectionHandle;
+ ULONG ViewSize = 0L;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ PVOID PsxSessionDataBaseAddress;
+
+ ConnectionInfoInLength = sizeof(ConnectionInfoIn);
+
+ CONSTRUCT_PSX_SES_NAME(SessionName, PSX_SES_BASE_PORT_PREFIX, UniqueId);
+
+ RtlInitAnsiString(&SessionPortName, SessionName);
+ RtlAnsiStringToUnicodeString(&SessionPortName_U, &SessionPortName, TRUE);
+
+ DynamicQos.ImpersonationLevel = SecurityImpersonation;
+ DynamicQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ DynamicQos.EffectiveOnly = TRUE;
+
+ //
+ // get the session communication port handle. this handle will be used
+ // to send console requests to psxses.exe for this session.
+ //
+
+ Status = NtConnectPort(&SessionPortHandle, &SessionPortName_U, &DynamicQos,
+ NULL, NULL, NULL, NULL, NULL);
+ RtlFreeUnicodeString(&SessionPortName_U);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXDLL: Unable to connect to %s - Status == %X\n",
+ SessionPortName.Buffer, Status));
+ return Status;
+ }
+
+
+ //
+ // open the session data section and map it to this process
+ //
+
+ CONSTRUCT_PSX_SES_NAME(SessionName, PSX_SES_BASE_DATA_PREFIX, UniqueId);
+
+ RtlInitAnsiString(&SessionDataName, SessionName);
+
+ Status = RtlAnsiStringToUnicodeString(&SessionDataName_U, &SessionDataName,
+ TRUE);
+ ASSERT(NT_SUCCESS(Status));
+
+ InitializeObjectAttributes(&ObjectAttributes, &SessionDataName_U, 0, NULL,
+ NULL);
+
+ Status = NtOpenSection(&SectionHandle, SECTION_MAP_WRITE,
+ &ObjectAttributes);
+
+ RtlFreeUnicodeString(&SessionDataName_U);
+
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ //
+ // Let MM locate the view
+ //
+
+ PsxSessionDataBaseAddress = 0;
+
+ Status = NtMapViewOfSection(SectionHandle, NtCurrentProcess(),
+ &PsxSessionDataBaseAddress, 0L, 0L, NULL,
+ &ViewSize, ViewUnmap, 0L, PAGE_READWRITE);
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ //
+ // record the session port in the PEB.
+ //
+ {
+ PPEB_PSX_DATA Peb;
+
+ Peb = (PPEB_PSX_DATA)(NtCurrentPeb()->SubSystemData);
+ Peb->SessionPortHandle = SessionPortHandle;
+ Peb->SessionDataBaseAddress = PsxSessionDataBaseAddress;
+ }
+
+ // BUGBUG! find cleanup code and close the port, or let exit cleanup
+
+ return Status;
+}
diff --git a/private/posix/client/conreqst.c b/private/posix/client/conreqst.c
new file mode 100644
index 000000000..5e51ecc07
--- /dev/null
+++ b/private/posix/client/conreqst.c
@@ -0,0 +1,56 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ conreqst.c
+
+Abstract:
+
+ This module implements the POSIX console API calls
+
+Author:
+
+ Avi Nathan (avin) 23-Jul-1991
+
+Revision History:
+
+ Ellen Aycock-Wright (ellena) 15-Sept-1991 Modified for POSIX
+
+--*/
+
+#include "psxdll.h"
+
+
+NTSTATUS
+SendConsoleRequest(IN OUT PSCREQUESTMSG Request)
+{
+ HANDLE SessionPort;
+ NTSTATUS Status;
+
+ PORT_MSG_TOTAL_LENGTH(*Request) = sizeof(SCREQUESTMSG);
+ PORT_MSG_DATA_LENGTH(*Request) = sizeof(SCREQUESTMSG) - sizeof(PORT_MESSAGE);
+ PORT_MSG_ZERO_INIT(*Request) = 0L;
+
+ SessionPort = ((PPEB_PSX_DATA)(NtCurrentPeb()->SubSystemData))->SessionPortHandle;
+
+ Status = NtRequestWaitReplyPort(SessionPort, (PPORT_MESSAGE)Request,
+ (PPORT_MESSAGE) Request);
+
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXDLL: Unable to send CON request: %X\n", Status));
+ if (0xffffffff == Status) {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // Probably somebody shot posix.exe, or he died for some other
+ // reason. We'll shoot the user's process for him.
+ //
+ _exit(99);
+ }
+ ASSERT(PORT_MSG_TYPE(*Request) == LPC_REPLY);
+
+ return Request->Status;
+}
diff --git a/private/posix/client/crtsup.c b/private/posix/client/crtsup.c
new file mode 100644
index 000000000..127950fef
--- /dev/null
+++ b/private/posix/client/crtsup.c
@@ -0,0 +1,233 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ crtsup.c
+
+Abstract:
+
+ This module contains support routines used by the Posix C runtimes.
+
+Author:
+
+ Ellen Aycock-Wright (ellena) 07-Aug-1991
+
+Environment:
+
+ User Mode only
+
+Revision History:
+
+--*/
+
+#include "psxmsg.h"
+#include "psxdll.h"
+
+
+char *
+_CRTAPI1
+__PdxGetCmdLine(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ The command line of the current process is available using this
+ API.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ The address of the current processes command line is returned. The
+ return value is a pointer to null terminate string.
+
+--*/
+
+{
+ return PsxAnsiCommandLine.Buffer;
+}
+
+int
+PdxStatusToErrno(
+ IN NTSTATUS Status
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure converts an NT status code to an
+ equivalent errno value. BUG BUG it is duplicated in the
+ server as PsxStatusToErrno to avoid calling the server.
+
+ The conversion is a function of the status code class.
+
+Arguments:
+
+ Class - Supplies the status code class to use.
+
+ Status - Supplies the status code to convert.
+
+Return Value:
+
+ Returns an equivalent error code to the supplied status code.
+
+--*/
+
+{
+ ULONG Error;
+
+ switch (Status) {
+
+ case STATUS_INVALID_PARAMETER:
+ Error = EINVAL;
+ break;
+
+ case STATUS_DIRECTORY_NOT_EMPTY:
+ // Error = ENOTEMPTY;
+ Error = EEXIST;
+ break;
+
+ case STATUS_OBJECT_PATH_INVALID:
+ case STATUS_NOT_A_DIRECTORY:
+ Error = ENOTDIR;
+ break;
+
+ case STATUS_OBJECT_PATH_SYNTAX_BAD:
+ // this for the rename test; 'old' has component too long.
+ Error = ENAMETOOLONG;
+ break;
+
+ case STATUS_OBJECT_NAME_COLLISION:
+ Error = EEXIST;
+ break;
+
+ case STATUS_OBJECT_PATH_NOT_FOUND:
+ case STATUS_OBJECT_NAME_NOT_FOUND:
+ case STATUS_DELETE_PENDING:
+ Error = ENOENT;
+ break;
+
+ case STATUS_NO_MEMORY:
+ case STATUS_INSUFFICIENT_RESOURCES:
+ Error = ENOMEM;
+ break;
+
+ case STATUS_CANNOT_DELETE:
+ Error = ETXTBUSY;
+ break;
+
+ case STATUS_DISK_FULL:
+ Error = ENOSPC;
+ break;
+
+ case STATUS_MEDIA_WRITE_PROTECTED:
+ Error = EROFS;
+ break;
+
+ case STATUS_OBJECT_NAME_INVALID:
+ Error = ENAMETOOLONG;
+ break;
+
+ case STATUS_FILE_IS_A_DIRECTORY:
+ Error = EISDIR;
+ break;
+
+ case STATUS_NOT_SAME_DEVICE:
+ Error = EXDEV;
+ break;
+
+ default :
+ Error = EACCES;
+ }
+
+ return Error;
+}
+
+//
+// Copied from the server side.
+//
+int
+PdxStatusToErrnoPath(
+ PUNICODE_STRING Path
+ )
+{
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES Obj;
+ HANDLE FileHandle;
+ ULONG DesiredAccess;
+ IO_STATUS_BLOCK Iosb;
+ ULONG Options;
+ PWCHAR pwc, pwcSav;
+ ULONG MinLen = sizeof(L"\\DosDevices\\X:\\");
+
+ DesiredAccess = SYNCHRONIZE;
+ Options = FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE;
+
+ pwcSav = NULL;
+
+ for (;;) {
+ //
+ // Remove trailing component.
+ //
+
+ pwc = wcsrchr(Path->Buffer, L'\\');
+
+ if (pwcSav)
+ *pwcSav = L'\\';
+
+ if (NULL == pwc) {
+ break;
+ }
+ *pwc = UNICODE_NULL;
+ pwcSav = pwc;
+
+ Path->Length = wcslen(Path->Buffer) * sizeof(WCHAR);
+
+ if (Path->Length <= MinLen) {
+ *pwcSav = L'\\';
+ break;
+ }
+
+ InitializeObjectAttributes(&Obj, Path, 0, NULL, NULL);
+
+ Status = NtOpenFile(&FileHandle, DesiredAccess, &Obj,
+ &Iosb,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ Options);
+ if (NT_SUCCESS(Status)) {
+ NtClose(FileHandle);\
+ }
+ if (STATUS_NOT_A_DIRECTORY == Status) {
+ *pwcSav = L'\\';
+ Path->Length = wcslen(Path->Buffer) * sizeof(WCHAR);
+ return ENOTDIR;
+ }
+ }
+ Path->Length = wcslen(Path->Buffer) * sizeof(WCHAR);
+ return ENOENT;
+}
+
+int _CRTAPI1
+raise(int sig)
+{
+ return kill(getpid(), sig);
+}
+
+/*
+ * This routine is called by heapinit(), in crt32psx/winheap. We
+ * would have a reference forwarder in psxdll.def, except RtlProcessHeap
+ * is a macro and can't be forwarded.
+ */
+void *
+GetProcessHeap(void)
+{
+ return (void *)RtlProcessHeap();
+}
diff --git a/private/posix/client/dllext.c b/private/posix/client/dllext.c
new file mode 100644
index 000000000..dcf9c8eb6
--- /dev/null
+++ b/private/posix/client/dllext.c
@@ -0,0 +1,88 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ dllext.c
+
+Abstract:
+
+ Client implementation of C Language Extensions (Chapter 8 of 1003.1)
+
+Author:
+
+ Ellen Aycock-Wright (ellena) 15-Oct-1991
+
+Revision History:
+
+--*/
+
+#include <stdio.h>
+#include <fcntl.h>
+#include "psxdll.h"
+
+extern FILE *_getstream(void);
+
+int
+_CRTAPI1
+fileno(FILE *stream)
+{
+ return(stream->_file);
+}
+
+#if 0
+FILE *
+fdopen(int fildes, const char *type)
+{
+ FILE *stream;
+ int mode;
+ int streamflag = 0;
+
+ //
+ // XXX.mjb: we need fcntl to check modes and validity of fildes
+ //
+
+ if (NULL == (stream = _getstream())) {
+ return NULL;
+ }
+ switch (*type) {
+ case 'r':
+ mode = O_RDONLY;
+ streamflag |= _IOREAD;
+ break;
+ case 'w':
+ mode = O_WRONLY;
+ streamflag |= _IOWRT;
+ break;
+ case 'a':
+ mode = O_WRONLY | O_APPEND;
+ streamflag |= _IOWRT;
+ // XXX.mjb: should be _IOWRT | _IOAPPEND;
+ break;
+ default:
+ errno = EINVAL;
+ return NULL;
+ }
+
+ switch (*++type) {
+ case '\0':
+ break;
+ case '+':
+ mode |= O_RDWR;
+ mode &= ~(O_RDONLY | O_WRONLY);
+ streamflag |= _IOWRT;
+ streamflag &= (_IOREAD | _IOWRT);
+ break;
+ default:
+ errno = EINVAL;
+ return NULL;
+ }
+
+ stream->_flag = streamflag;
+ stream->_cnt = 0;
+ stream->_base = stream->_ptr = NULL;
+ stream->_file = fildes;
+ return stream;
+}
+#endif
diff --git a/private/posix/client/dllfile.c b/private/posix/client/dllfile.c
new file mode 100644
index 000000000..14b7a3d54
--- /dev/null
+++ b/private/posix/client/dllfile.c
@@ -0,0 +1,1011 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dllfile.c
+
+Abstract:
+
+ Client implementation of File and Directory functions for POSIX.
+
+Author:
+
+ Mark Lucovsky (markl) 15-Dec-1989
+
+Revision History:
+
+--*/
+
+#include <unistd.h>
+#include <sys/stat.h>
+#include "psxdll.h"
+
+int _CRTAPI1
+closedir(DIR *dirp)
+{
+ int r = 0;
+
+ try {
+ if (-1 == close(dirp->Directory)) {
+ return -1;
+ }
+ dirp->Directory = -1;
+ dirp->Index = (unsigned long)-1;
+
+ RtlFreeHeap(PdxHeap, 0, (PVOID)dirp);
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ r = -1;
+ }
+
+ return r;
+}
+
+DIR * _CRTAPI1
+opendir(const char *dirname)
+{
+ DIR *ReturnedDir;
+ int fd, i;
+
+ ReturnedDir = RtlAllocateHeap(PdxHeap, 0, sizeof(DIR));
+ if (NULL == ReturnedDir) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ fd = open(dirname, O_RDONLY);
+ if (-1 == fd) {
+ RtlFreeHeap(PdxHeap, 0, (PVOID)ReturnedDir);
+ return NULL;
+ }
+
+ i = fcntl(fd, F_SETFD, FD_CLOEXEC);
+ if (0 != i) {
+ close(fd);
+ RtlFreeHeap(PdxHeap, 0, (PVOID)ReturnedDir);
+ return NULL;
+ }
+
+ ReturnedDir->Directory = fd;
+ ReturnedDir->Dirent.d_name[0] = '\0';
+ ReturnedDir->Index = 0;
+ ReturnedDir->RestartScan = FALSE;
+
+ return ReturnedDir;
+}
+
+struct dirent * _CRTAPI1
+readdir(DIR *dirp)
+{
+ PSX_API_MSG m;
+ PPSX_READDIR_MSG args;
+ NTSTATUS Status;
+ char *buf;
+
+ args = &m.u.ReadDir;
+
+ buf = &dirp->Dirent.d_name[0];
+
+again:
+ for (;;) {
+ PSX_FORMAT_API_MSG(m, PsxReadDirApi, sizeof(*args));
+ args->FileDes = dirp->Directory;
+ args->Buf = buf;
+ args->Nbytes = PATH_MAX;
+ args->RestartScan = dirp->RestartScan;
+ dirp->RestartScan = 0;
+
+ Status = NtRequestWaitReplyPort(PsxPortHandle,
+ (PPORT_MESSAGE)&m, (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(Status));
+#endif
+
+ if (EINTR == m.Error && SIGCONT == m.Signal) {
+ //
+ // The system call was stopped and continued. Call
+ // again instead of returning EINTR.
+ //
+ continue;
+ }
+ if (m.Error) {
+ errno = m.Error;
+ return NULL;
+ }
+ break;
+ }
+
+ if (0 == m.ReturnValue) {
+ return NULL;
+ }
+
+ //
+ // Skip dot and dot-dot.
+ //
+
+ if (m.ReturnValue <= 2 && buf[0] == '.') {
+ if (m.ReturnValue == 1 || buf[1] == '.') {
+ goto again;
+ }
+ }
+
+ try {
+ ++dirp->Index;
+
+ dirp->Dirent.d_name[m.ReturnValue] = '\0';
+ return &dirp->Dirent;
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ errno = EFAULT;
+ }
+ // we've taken an exception.
+ return NULL;
+}
+
+void
+_CRTAPI1
+rewinddir(DIR *dirp)
+{
+ dirp->RestartScan = TRUE;
+ dirp->Index = 0;
+}
+
+int _CRTAPI1
+chdir(const char *path)
+{
+ NTSTATUS Status;
+ HANDLE Directory;
+ IO_STATUS_BLOCK Iosb;
+ OBJECT_ATTRIBUTES ObjA;
+ UNICODE_STRING Path_U;
+ ANSI_STRING Path_A;
+ PANSI_STRING pCWD;
+ auto sigset_t set, oset;
+ int ret_val = 0;
+
+ if (!PdxCanonicalize((PSZ)path, &Path_U, PdxHeap)) {
+ return -1;
+ }
+ InitializeObjectAttributes(&ObjA, &Path_U, OBJ_INHERIT, NULL, NULL);
+
+ //
+ // Make sure that the path is to a directory
+ //
+
+ Status = NtOpenFile(&Directory, SYNCHRONIZE, &ObjA, &Iosb,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
+ if (!NT_SUCCESS(Status)) {
+ if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
+ errno = PdxStatusToErrnoPath(&Path_U);
+ } else {
+ errno = PdxStatusToErrno(Status);
+ }
+ RtlFreeHeap(PdxHeap, 0, (PVOID)Path_U.Buffer);
+ return -1;
+ }
+
+ Status = NtClose(Directory);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXDLL: NtClose: 0x%x\n", Status));
+ }
+
+ RtlUnicodeStringToAnsiString(&Path_A, &Path_U, TRUE);
+ RtlFreeHeap(PdxHeap, 0, (PVOID)Path_U.Buffer);
+
+ pCWD = &PdxDirectoryPrefix.NtCurrentWorkingDirectory;
+
+ //
+ // The path was opened ok. Make sure that there is space for the
+ // pathname in the PdxDirectoryPrefix buffer.
+ //
+
+ if (Path_A.Length > pCWD->MaximumLength + 2) {
+ RtlFreeAnsiString(&Path_A);
+ errno = ENOENT;
+ return -1;
+ }
+
+ //
+ // Keep the process from trying to use his CWD while we're modifying
+ // it.
+ //
+
+ sigfillset(&set);
+ sigprocmask(SIG_BLOCK, &set, &oset);
+
+ //
+ // Update NtCurrentWorkingDirectory
+ //
+
+ RtlMoveMemory(pCWD->Buffer, Path_A.Buffer, Path_A.Length);
+ if ('\\' != pCWD->Buffer[Path_A.Length - 1]) {
+ pCWD->Buffer[Path_A.Length] = '\\';
+ pCWD->Buffer[Path_A.Length + 1] = '\0';
+ pCWD->Length = Path_A.Length + 1;
+ } else {
+ pCWD->Buffer[Path_A.Length + 1] = '\0';
+ pCWD->Length = Path_A.Length;
+ }
+
+ //
+ // Set length of translated current working directory to zero.
+ // getcwd() uses this as its hint to translate NtCurrentWorkingDirectory
+ // to PsxCurrentWorkingDirectory.
+ //
+
+ PdxDirectoryPrefix.PsxCurrentWorkingDirectory.Length = 0;
+
+ //
+ // Update the PsxRoot.
+ //
+
+ RtlMoveMemory(PdxDirectoryPrefix.PsxRoot.Buffer, Path_A.Buffer,
+ PdxDirectoryPrefix.PsxRoot.Length);
+
+ RtlFreeAnsiString(&Path_A);
+ sigprocmask(SIG_SETMASK, &oset, NULL);
+
+ return 0;
+}
+
+char *
+_CRTAPI1
+getcwd(char *buf, size_t size)
+{
+ USHORT i, j, CwdSize;
+ PANSI_STRING pPsxCwd, pNtCwd, pPsxRoot;
+
+ if (size <= 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ //
+ // Note that NtCwd should always have a trailing backslash.
+ //
+
+ pNtCwd = &PdxDirectoryPrefix.NtCurrentWorkingDirectory;
+ pPsxCwd = &PdxDirectoryPrefix.PsxCurrentWorkingDirectory;
+ pPsxRoot = &PdxDirectoryPrefix.PsxRoot;
+
+ CwdSize = pNtCwd->Length - pPsxRoot->Length;
+ if (1 == CwdSize) {
+ //
+ // If the CWD is "/", then we'll have a trailing slash and
+ // we'll need space for it.
+ //
+ ++CwdSize;
+ }
+ if (size < CwdSize) {
+ errno = ERANGE;
+ return NULL;
+ }
+
+ if (0 == pPsxCwd->Length) {
+ for (i = 0, j = pPsxRoot->Length; i < CwdSize - 1; i++, j++) {
+ pPsxCwd->Buffer[i] = (pNtCwd->Buffer[j] == '\\') ?
+ '/' : pNtCwd->Buffer[j];
+ }
+ pPsxCwd->Buffer[CwdSize] = '\0';
+ pPsxCwd->Length = CwdSize - 1;
+
+ }
+
+ try {
+ RtlMoveMemory(buf, pPsxCwd->Buffer, pPsxCwd->Length);
+ buf[pPsxCwd->Length] = '\0';
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ errno = EFAULT;
+ buf = NULL;
+ }
+
+ return buf;
+}
+
+mode_t
+_CRTAPI1
+umask(mode_t cmask)
+{
+ PSX_API_MSG m;
+ NTSTATUS Status;
+ PPSX_UMASK_MSG args;
+
+ args = &m.u.Umask;
+ PSX_FORMAT_API_MSG(m, PsxUmaskApi, sizeof(*args));
+
+ args->Cmask = cmask;
+
+ Status = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(Status));
+#endif
+
+ if (m.Error) {
+ errno = (int)m.Error;
+ return (mode_t)-1;
+ }
+ return (mode_t)m.ReturnValue;
+}
+
+int
+_CRTAPI1
+mkdir(const char *path, mode_t mode)
+{
+ PSX_API_MSG m;
+ NTSTATUS Status;
+ PPSX_MKDIR_MSG args;
+ UNICODE_STRING Path_U;
+
+ args = &m.u.MkDir;
+ PSX_FORMAT_API_MSG(m, PsxMkDirApi, sizeof(*args));
+
+ if (!PdxCanonicalize((PSZ)path, &Path_U, PdxPortHeap)) {
+ return -1;
+ }
+
+ args->Path_U = Path_U;
+ args->Path_U.Buffer = (PVOID)((PCHAR)Path_U.Buffer +
+ PsxPortMemoryRemoteDelta);
+ args->Mode = mode;
+
+ Status = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(Status));
+#endif
+
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)Path_U.Buffer);
+
+ if (m.Error) {
+ errno = (int)m.Error;
+ return -1;
+ }
+ return (int)m.ReturnValue;
+}
+
+
+int
+_CRTAPI1
+mkfifo(const char *path, mode_t mode)
+{
+ PSX_API_MSG m;
+ NTSTATUS Status;
+ PPSX_MKFIFO_MSG args;
+ PVOID p;
+
+ args = &m.u.MkFifo;
+
+ PSX_FORMAT_API_MSG(m,PsxMkFifoApi,sizeof(*args));
+
+ if (!PdxCanonicalize((PSZ)path, &args->Path_U, PdxPortHeap)) {
+ return -1;
+ }
+
+ p = args->Path_U.Buffer;
+ args->Path_U.Buffer = (PWSTR)((PCHAR)p + PsxPortMemoryRemoteDelta);
+ args->Mode = mode;
+
+ Status = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(Status));
+#endif
+
+ RtlFreeHeap(PdxPortHeap, 0, p);
+
+ if (m.Error) {
+ errno = (int)m.Error;
+ return -1;
+ }
+ return m.ReturnValue;
+}
+
+int
+_CRTAPI1
+rmdir(const char *path)
+{
+ PSX_API_MSG m;
+ NTSTATUS Status;
+ PPSX_RMDIR_MSG args;
+ PVOID p;
+
+ args = &m.u.RmDir;
+
+ PSX_FORMAT_API_MSG(m,PsxRmDirApi,sizeof(*args));
+
+ if (!PdxCanonicalize((PSZ)path, &args->Path_U, PdxPortHeap)) {
+ return -1;
+ }
+
+ p = args->Path_U.Buffer;
+ args->Path_U.Buffer = (PWSTR)((PCHAR)p + PsxPortMemoryRemoteDelta);
+
+ Status = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(Status));
+#endif
+
+ RtlFreeHeap(PdxPortHeap, 0, p);
+
+ if (m.Error) {
+ errno = (int)m.Error;
+ return -1;
+ }
+ return (int)m.ReturnValue;
+}
+
+int
+_CRTAPI1
+stat(const char *path, struct stat *buf)
+{
+ PSX_API_MSG m;
+ NTSTATUS Status;
+ PPSX_STAT_MSG args;
+ struct stat *tmpbuf;
+ void *p;
+ int r;
+
+ args = &m.u.Stat;
+ PSX_FORMAT_API_MSG(m, PsxStatApi, sizeof(*args));
+
+ if (!PdxCanonicalize((PSZ)path, &args->Path_U, PdxPortHeap)) {
+ return -1;
+ }
+
+ p = args->Path_U.Buffer;
+ args->Path_U.Buffer = (PWSTR)((PCHAR)p + PsxPortMemoryRemoteDelta);
+
+ tmpbuf = RtlAllocateHeap(PdxPortHeap, 0, sizeof(struct stat));
+ ASSERT(NULL != tmpbuf);
+
+ args->StatBuf = (struct stat *)((PCHAR)tmpbuf + PsxPortMemoryRemoteDelta);
+ Status = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(Status));
+#endif
+
+ RtlFreeHeap(PdxPortHeap, 0, p);
+
+ if (m.Error) {
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)tmpbuf);
+ errno = (int)m.Error;
+ return -1;
+ }
+
+ r = 0;
+
+ try {
+ (void)memcpy(buf, tmpbuf, sizeof(struct stat));
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ r = -1;
+ errno = EFAULT;
+ }
+
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)tmpbuf);
+ return r;
+}
+
+int
+_CRTAPI1
+fstat(int fildes, struct stat *buf)
+{
+ PSX_API_MSG m;
+ NTSTATUS Status;
+ PPSX_FSTAT_MSG args;
+ struct stat *tmpbuf;
+ int r;
+
+ args = &m.u.FStat;
+ PSX_FORMAT_API_MSG(m, PsxFStatApi, sizeof(*args));
+
+ args->FileDes = fildes;
+
+ tmpbuf = RtlAllocateHeap(PdxPortHeap, 0, sizeof(struct stat));
+ ASSERT(NULL != tmpbuf);
+
+ args->StatBuf = (struct stat *)((PCHAR)tmpbuf + PsxPortMemoryRemoteDelta);
+ Status = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(Status));
+#endif
+
+ if (m.Error) {
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)tmpbuf);
+ errno = (int)m.Error;
+ return -1;
+ }
+
+ r = 0;
+
+ try {
+ (void)memcpy(buf, tmpbuf, sizeof(struct stat));
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ r = -1;
+ errno = EFAULT;
+ }
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)tmpbuf);
+ return r;
+}
+
+int
+_CRTAPI1
+access(const char *path, int amode)
+{
+ PSX_API_MSG m;
+ NTSTATUS Status;
+
+ PPSX_ACCESS_MSG args;
+
+ if (0 != (amode & ~(W_OK | R_OK | X_OK))) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ args = &m.u.Access;
+
+ PSX_FORMAT_API_MSG(m,PsxAccessApi,sizeof(*args));
+
+ if (!PdxCanonicalize((PSZ)path, &args->Path_U, PdxPortHeap)) {
+ return -1;
+ }
+
+ m.DataBlock = args->Path_U.Buffer;
+ args->Path_U.Buffer = (PWSTR)((PCHAR)m.DataBlock + PsxPortMemoryRemoteDelta);
+ args->Amode = amode;
+
+ Status = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(Status));
+#endif
+
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)m.DataBlock);
+
+ if (m.Error) {
+ errno = (int)m.Error;
+ return -1;
+ }
+ return m.ReturnValue;
+}
+
+int
+_CRTAPI1
+chmod(const char *path, mode_t mode)
+{
+ PSX_API_MSG m;
+ NTSTATUS Status;
+ PPSX_CHMOD_MSG args;
+
+ args = &m.u.Chmod;
+
+ PSX_FORMAT_API_MSG(m,PsxChmodApi,sizeof(*args));
+
+ if (!PdxCanonicalize((PSZ)path, &args->Path_U, PdxPortHeap)) {
+ return -1;
+ }
+
+ m.DataBlock = args->Path_U.Buffer;
+ args->Path_U.Buffer = (PWSTR)((PCHAR)m.DataBlock + PsxPortMemoryRemoteDelta);
+ args->Mode = mode;
+
+ Status = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(Status));
+#endif
+
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)m.DataBlock);
+
+ if (m.Error) {
+ errno = (int)m.Error;
+ return -1;
+ }
+ return m.ReturnValue;
+}
+
+int
+_CRTAPI1
+chown(const char *path, uid_t owner, gid_t group)
+{
+ PSX_API_MSG m;
+ NTSTATUS Status;
+ PPSX_CHOWN_MSG args;
+
+ args = &m.u.Chown;
+
+ PSX_FORMAT_API_MSG(m, PsxChownApi, sizeof(*args));
+
+ if (!PdxCanonicalize((PSZ)path, &args->Path_U, PdxPortHeap)) {
+ return -1;
+ }
+
+ m.DataBlock = args->Path_U.Buffer;
+ args->Path_U.Buffer = (PWSTR)((PCHAR)m.DataBlock + PsxPortMemoryRemoteDelta);
+ args->Owner = owner;
+ args->Group = group;
+
+ Status = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(Status));
+#endif
+
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)m.DataBlock);
+
+ if (m.Error) {
+ errno = (int)m.Error;
+ return -1;
+ }
+ return m.ReturnValue;
+}
+
+int
+_CRTAPI1
+utime(const char *path, const struct utimbuf *times)
+{
+ PSX_API_MSG m;
+ NTSTATUS Status;
+
+ PPSX_UTIME_MSG args;
+
+ args = &m.u.Utime;
+
+ PSX_FORMAT_API_MSG(m, PsxUtimeApi, sizeof(*args));
+
+ if (!PdxCanonicalize((PSZ)path, &args->Path_U, PdxPortHeap)) {
+ return -1;
+ }
+
+ m.DataBlock = args->Path_U.Buffer;
+ args->Path_U.Buffer = (PWSTR)((PCHAR)m.DataBlock +
+ PsxPortMemoryRemoteDelta);
+ args->TimesSpecified = (struct utimbuf *)times;
+
+ if (NULL != times) {
+ args->Times = *times;
+ }
+
+ Status = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(Status));
+#endif
+
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)m.DataBlock);
+
+ if (m.Error) {
+ errno = (int)m.Error;
+ return -1;
+ }
+ return m.ReturnValue;
+}
+
+long
+_CRTAPI1
+pathconf(const char *path, int name)
+{
+ PSX_API_MSG m;
+ NTSTATUS Status;
+ PPSX_PATHCONF_MSG args;
+
+ args = &m.u.PathConf;
+ PSX_FORMAT_API_MSG(m, PsxPathConfApi, sizeof(*args));
+
+ if (!PdxCanonicalize((PSZ)path, &args->Path, PdxPortHeap)) {
+ return -1;
+ }
+
+ m.DataBlock = args->Path.Buffer;
+ args->Path.Buffer = (PWSTR)((PCHAR)m.DataBlock + PsxPortMemoryRemoteDelta);
+ args->Name = name;
+
+ Status = NtRequestWaitReplyPort(PsxPortHandle,
+ (PPORT_MESSAGE)&m, (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(Status));
+#endif
+
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)m.DataBlock);
+
+ if (m.Error) {
+ errno = (int)m.Error;
+ return -1;
+ }
+ return((long)(m.ReturnValue));
+}
+
+long
+_CRTAPI1
+fpathconf(int fildes, int name)
+{
+ PSX_API_MSG m;
+ NTSTATUS Status;
+ PPSX_FPATHCONF_MSG args;
+
+ args = &m.u.FPathConf;
+ PSX_FORMAT_API_MSG(m, PsxFPathConfApi, sizeof(*args));
+
+ args->FileDes = fildes;
+ args->Name = name;
+
+ Status = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(Status));
+#endif
+
+ if (m.Error) {
+ errno = (int)m.Error;
+ return -1;
+ }
+ return m.ReturnValue;
+}
+
+int _CRTAPI1
+rename(const char *old, const char *new)
+{
+ NTSTATUS Status;
+ UNICODE_STRING old_U, new_U;
+ PSX_API_MSG m;
+ PPSX_RENAME_MSG args;
+ sigset_t set, oset;
+ int r; // ret val
+ static char path[PATH_MAX];
+ char *pch, c;
+ WCHAR *pwc;
+ int i;
+ struct stat st_buf1, st_buf2;
+ static int been_here = 0; // prevent infinite recursion
+
+ args = &m.u.Rename;
+ PSX_FORMAT_API_MSG(m, PsxRenameApi, sizeof(*args));
+
+ if (!PdxCanonicalize((PSZ)old, &old_U, PdxPortHeap)) {
+ return -1;
+ }
+
+ if (!PdxCanonicalize((PSZ)new, &new_U, PdxPortHeap)) {
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)old_U.Buffer);
+ return -1;
+ }
+
+ //
+ // 1003.1-90 (5.5.3.4): EISDIR ... The /new/ argument points
+ // to a directory, and the /old/ argument points to a file that
+ // is not a directory.
+ //
+ // ENOTDIR ... the /old/ argument names a
+ // directory and the /new/ argument names a nondirectory file.
+ //
+
+ i = errno;
+ if (0 == stat(old, &st_buf1) && 0 == stat(new, &st_buf2)) {
+ if (S_ISDIR(st_buf2.st_mode) && S_ISREG(st_buf1.st_mode)) {
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)old_U.Buffer);
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)new_U.Buffer);
+ errno = EISDIR;
+ return -1;
+ }
+ if (S_ISREG(st_buf2.st_mode) && S_ISDIR(st_buf1.st_mode)) {
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)old_U.Buffer);
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)new_U.Buffer);
+ errno = ENOTDIR;
+ return -1;
+ }
+ }
+ errno = i;
+
+ //
+ // 1003.1-90 (5.5.3.4): EINVAL ... The /new/ directory
+ // pathname contains a path prefix that names the /old/ directory.
+ //
+
+ pwc = wcsrchr(new_U.Buffer, L'\\');
+ ASSERT(NULL != pwc);
+ *pwc = 0;
+
+ if (0 == wcsncmp(new_U.Buffer, old_U.Buffer, old_U.Length)) {
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)old_U.Buffer);
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)new_U.Buffer);
+ errno = EINVAL;
+ return -1;
+ }
+ *pwc = L'\\'; // put it back
+
+ args->OldName = old_U;
+ args->NewName = new_U;
+
+ args->OldName.Buffer =
+ (PVOID)((PCHAR)old_U.Buffer + PsxPortMemoryRemoteDelta);
+ args->NewName.Buffer =
+ (PVOID)((PCHAR)new_U.Buffer + PsxPortMemoryRemoteDelta);
+
+ Status = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(Status));
+#endif
+
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)new_U.Buffer);
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)old_U.Buffer);
+
+ if (0 == m.Error) {
+ return m.ReturnValue;
+ }
+ if (EACCES != m.Error) {
+ errno = m.Error;
+ return -1;
+ }
+
+ //
+ // The rename operation failed because the target already
+ // exists. This happens when trying to rename a directory
+ // over an existing directory, which POSIX requires but
+ // NT filesystems don't support. We emulate here.
+ //
+
+ if (been_here) {
+ errno = EACCES;
+ return -1;
+ }
+ been_here++;
+
+ // block all signals during the operation.
+
+ sigfillset(&set);
+ sigprocmask(SIG_SETMASK, &set, &oset);
+
+ r = 0;
+
+ //
+ // Figure out a temporary pathname to use. The temporary
+ // dir is created in the same directory as 'new'.
+ //
+
+ strcpy(path, new);
+
+ // take care of paths that end in slash...
+
+ for (;;) {
+ i = strlen(path) - 1;
+ if ('/' == path[i]) {
+ path[i] = '\0';
+ } else {
+ break;
+ }
+ }
+
+ pch = strrchr(path, '/');
+ if (NULL != pch) {
+ ++pch;
+ strcpy(pch, "_psxtmp.d");
+ } else {
+ // 'new' is in the cwd
+
+ strcpy(path, "_psxtmp.d");
+ pch = path;
+ }
+
+ for (c = 'a'; ; c++) {
+ if (c > 'z') {
+ errno = EEXIST;
+ return -1;
+ }
+ *pch = c;
+
+ if (-1 == (r = rename(new, path))) {
+ if (EEXIST == errno) {
+ // try the next letter for tmp path
+ continue;
+ }
+ errno = EACCES; // reset errno
+ break;
+ }
+ if (-1 == (r = rename(old, new))) {
+ (void)rename(path, new);
+ break;
+ }
+ if (-1 == rmdir(path)) {
+ if (-1 == (r = rename(new, old))) {
+ //
+ // If we don't bail here, the following call
+ // to rename will recurse infinitely.
+ //
+ break;
+ }
+ (void)rename(path, new);
+ r = -1;
+ break;
+ }
+ break;
+ }
+ been_here = 0;
+ sigprocmask(SIG_SETMASK, &oset, NULL);
+ return r;
+}
+
+int
+_CRTAPI1
+unlink(const char *path)
+{
+ PSX_API_MSG m;
+ NTSTATUS Status;
+ PPSX_UNLINK_MSG args;
+
+ args = &m.u.Unlink;
+ PSX_FORMAT_API_MSG(m, PsxUnlinkApi, sizeof(*args));
+
+ if (!PdxCanonicalize((PSZ)path, &args->Path_U, PdxPortHeap)) {
+ return -1;
+ }
+
+ m.DataBlock = args->Path_U.Buffer;
+ args->Path_U.Buffer = (PWSTR)((PCHAR)m.DataBlock +
+ PsxPortMemoryRemoteDelta);
+
+ Status = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(Status));
+#endif
+
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)m.DataBlock);
+
+ if (m.Error) {
+ errno = (int)m.Error;
+ return -1;
+ }
+ return 0;
+}
+
+int
+_CRTAPI1
+link(const char *existing, const char *new)
+{
+ PPSX_LINK_MSG args;
+ PSX_API_MSG m;
+ UNICODE_STRING old_U, new_U;
+ NTSTATUS Status;
+
+ args = &m.u.Link;
+ PSX_FORMAT_API_MSG(m, PsxLinkApi, sizeof(*args));
+
+ if (!PdxCanonicalize((PSZ)existing, &old_U, PdxPortHeap)) {
+ return -1;
+ }
+
+ if (!PdxCanonicalize((PSZ)new, &new_U, PdxPortHeap)) {
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)old_U.Buffer);
+ return -1;
+ }
+
+ args->OldName = old_U;
+ args->NewName = new_U;
+
+ args->OldName.Buffer =
+ (PVOID)((PCHAR)old_U.Buffer + PsxPortMemoryRemoteDelta);
+ args->NewName.Buffer =
+ (PVOID)((PCHAR)new_U.Buffer + PsxPortMemoryRemoteDelta);
+
+ Status = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(Status));
+#endif
+
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)new_U.Buffer);
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)old_U.Buffer);
+
+ if (0 != m.Error) {
+ errno = m.Error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/private/posix/client/dllinit.c b/private/posix/client/dllinit.c
new file mode 100644
index 000000000..8430706b9
--- /dev/null
+++ b/private/posix/client/dllinit.c
@@ -0,0 +1,377 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dllinit.c
+
+Abstract:
+
+ This module contains the initialization code for the POSIX Subsystem
+ Client DLL.
+
+Author:
+
+ Mark Lucovsky (markl) 27-Jun-1989
+
+Environment:
+
+ User Mode only
+
+Revision History:
+
+ Ellen Aycock-Wright (ellena) 03-Jan-1991
+ Converted to DLL initialization routine.
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include "psxdll.h"
+
+extern void ClientOpen(int);
+
+ULONG PsxPortMemoryRemoteDelta;
+PVOID PsxPortMemoryBase;
+
+BOOLEAN
+PsxDllInitialize(
+ IN PVOID DllHandle,
+ IN ULONG Reason,
+ IN PCONTEXT Context OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the DLL initialization routine for the POSIX Emulation
+ Subsystem Client DLL. This function gets control when the applications
+ links to this DLL are snapped.
+
+Arguments:
+
+ Context - Supplies an optional context buffer that will be restored
+ after all DLL initialization has been completed. If this
+ parameter is NULL then this is a dynamic snap of this module.
+ Otherwise this is a static snap prior to the user process
+ gaining control.
+
+Return Value:
+
+ False if initialization failed.
+
+--*/
+{
+ PPEB Peb;
+ PPEB_PSX_DATA PebPsxData;
+ NTSTATUS Status;
+
+ if (Reason != DLL_PROCESS_ATTACH) {
+ return TRUE;
+ }
+
+ //
+ // Remember our DLL handle in a global variable.
+ //
+
+ PsxDllHandle = DllHandle;
+
+ PdxHeap = RtlCreateHeap( HEAP_GROWABLE | HEAP_NO_SERIALIZE,
+ NULL,
+ 64 * 1024, // Initial size of heap is 64K
+ 4 * 1024,
+ 0,
+ NULL
+ );
+
+ if (PdxHeap == NULL) {
+ return FALSE;
+ }
+
+ Status = PsxInitDirectories();
+ if ( !NT_SUCCESS( Status )) {
+ return FALSE;
+ }
+
+ Status = PsxConnectToServer();
+ if (!NT_SUCCESS(Status)) {
+ return FALSE;
+ }
+
+ Peb = NtCurrentPeb();
+
+ //
+ // This is not really an ANSI_STRING but an undocumented data
+ // structure. Read crt32psx\startup\crt0.c for the code that
+ // interprets this.
+ //
+
+
+ PsxAnsiCommandLine = *(PANSI_STRING)&(Peb->ProcessParameters->CommandLine);
+ if (ARGUMENT_PRESENT(Context)) {
+ PebPsxData = (PPEB_PSX_DATA)Peb->SubSystemData;
+ PebPsxData->ClientStartAddress = (PVOID)CONTEXT_TO_PROGRAM_COUNTER(Context);
+ (PVOID)CONTEXT_TO_PROGRAM_COUNTER(Context) = (PVOID)PdxProcessStartup;
+ }
+
+ return TRUE;
+}
+
+NTSTATUS
+PsxInitDirectories()
+{
+
+ PdxDirectoryPrefix.NtCurrentWorkingDirectory.Buffer =
+ RtlAllocateHeap(PdxHeap, 0,2*PATH_MAX);
+ PdxDirectoryPrefix.NtCurrentWorkingDirectory.Length = 0;
+ PdxDirectoryPrefix.NtCurrentWorkingDirectory.MaximumLength = 2*PATH_MAX;
+
+ PdxDirectoryPrefix.PsxCurrentWorkingDirectory.Buffer =
+ RtlAllocateHeap(PdxHeap, 0,PATH_MAX+1);
+ PdxDirectoryPrefix.PsxCurrentWorkingDirectory.Length = 0;
+ PdxDirectoryPrefix.PsxCurrentWorkingDirectory.MaximumLength = PATH_MAX+1;
+
+ PdxDirectoryPrefix.PsxRoot.Buffer = RtlAllocateHeap(PdxHeap, 0,2*PATH_MAX);
+ PdxDirectoryPrefix.PsxRoot.Length = 0;
+ PdxDirectoryPrefix.PsxRoot.MaximumLength = 2*PATH_MAX;
+
+ //
+ // Check that memory allocations worked. If not, then bail out
+ //
+
+ ASSERT(PdxDirectoryPrefix.NtCurrentWorkingDirectory.Buffer);
+ ASSERT(PdxDirectoryPrefix.PsxCurrentWorkingDirectory.Buffer);
+ ASSERT(PdxDirectoryPrefix.PsxRoot.Buffer);
+
+ if ( PdxDirectoryPrefix.NtCurrentWorkingDirectory.Buffer == NULL |
+ PdxDirectoryPrefix.PsxCurrentWorkingDirectory.Buffer== NULL |
+ PdxDirectoryPrefix.PsxRoot.Buffer == NULL ) {
+
+ return ( STATUS_NO_MEMORY );
+ }
+ return ( STATUS_SUCCESS );
+}
+
+NTSTATUS
+PsxConnectToServer(VOID)
+{
+ UNICODE_STRING PsxPortName;
+ PSX_API_CONNECTINFO ConnectionInformation;
+ ULONG ConnectionInformationLength;
+ PULONG AmIBeingDebugged;
+ REMOTE_PORT_VIEW ServerView;
+ HANDLE PortSection;
+ PPEB Peb;
+ PPEB_PSX_DATA PebPsxData;
+ PORT_VIEW ClientView;
+ LARGE_INTEGER SectionSize;
+ SECURITY_QUALITY_OF_SERVICE DynamicQos;
+ NTSTATUS Status;
+
+ ConnectionInformationLength = sizeof(ConnectionInformation);
+
+ //
+ // Create a section to contain the Port Memory. Port Memory is private
+ // memory that is shared between the POSIX client and server processes.
+ // This allows data that is too large to fit into an API request message
+ // to be passed to the POSIX server.
+ //
+
+ SectionSize.LowPart = PSX_CLIENT_PORT_MEMORY_SIZE;
+ SectionSize.HighPart = 0;
+
+ // SEC_RESERVE
+
+ Status = NtCreateSection(&PortSection, SECTION_ALL_ACCESS, NULL,
+ &SectionSize, PAGE_READWRITE, SEC_COMMIT, NULL);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXDLL: NtCreateSection: 0x%x\n", Status));
+ return Status;
+ }
+
+ //
+ // Get the Peb address. Allocate the POSIX subsystem specific portion
+ // within the Peb. This structure will be filled in by the server
+ // process as part of the connect logic.
+ //
+
+ Peb = NtCurrentPeb();
+ Peb->SubSystemData = RtlAllocateHeap(Peb->ProcessHeap, 0,
+ sizeof(PEB_PSX_DATA));
+ ASSERT(NULL != Peb->SubSystemData);
+
+ PebPsxData = (PPEB_PSX_DATA)Peb->SubSystemData;
+ PebPsxData->Length = sizeof(PEB_PSX_DATA);
+
+ //
+ // Connect to the POSIX Emulation Subsystem server. This includes a
+ // description of the Port Memory section so that the LPC connection
+ // logic can make the section visible to both the client and server
+ // processes. Also pass information the POSIX server needs in the
+ // connection information structure.
+ //
+
+ ClientView.Length = sizeof(ClientView);
+ ClientView.SectionHandle = PortSection;
+ ClientView.SectionOffset = 0;
+ ClientView.ViewSize = SectionSize.LowPart;
+ ClientView.ViewBase = 0;
+ ClientView.ViewRemoteBase = 0;
+
+ ServerView.Length = sizeof(ServerView);
+ ServerView.ViewSize = 0;
+ ServerView.ViewBase = 0;
+
+ ConnectionInformation.SignalDeliverer = _PdxSignalDeliverer;
+ ConnectionInformation.NullApiCaller = _PdxNullApiCaller;
+ ConnectionInformation.DirectoryPrefix = &PdxDirectoryPrefix;
+ ConnectionInformation.InitialPebPsxData.Length = PebPsxData->Length;
+
+ //
+ // Set up the security quality of service parameters to use over the
+ // port.
+ //
+
+ DynamicQos.ImpersonationLevel = SecurityImpersonation;
+ DynamicQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ DynamicQos.EffectiveOnly = TRUE;
+
+ RtlInitUnicodeString(&PsxPortName, PSX_SS_API_PORT_NAME);
+
+ Status = NtConnectPort(&PsxPortHandle, &PsxPortName, &DynamicQos,
+ &ClientView, &ServerView, NULL,
+ (PVOID)&ConnectionInformation,
+ (PULONG)&ConnectionInformationLength);
+
+ NtClose(PortSection);
+
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXDLL: Unable to connect to Posix server: %lx\n",
+ Status));
+ return Status;
+ }
+
+ Status = NtRegisterThreadTerminatePort(PsxPortHandle);
+ ASSERT(NT_SUCCESS(Status));
+
+ PsxPortMemoryBase = ClientView.ViewBase;
+ PsxPortMemoryRemoteDelta = (ULONG)ClientView.ViewRemoteBase -
+ (ULONG)ClientView.ViewBase;
+
+ RtlMoveMemory((PVOID)PebPsxData,
+ (PVOID)&ConnectionInformation.InitialPebPsxData,
+ PebPsxData->Length);
+
+ PdxPortHeap = RtlCreateHeap( HEAP_NO_SERIALIZE,
+ ClientView.ViewBase,
+ ClientView.ViewSize,
+ ClientView.ViewSize,
+ 0,
+ 0
+ );
+
+ if (PdxPortHeap == NULL) {
+ KdPrint(("PsxConnectToServer: RtlCreateHeap failed\n"));
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Connect to the session console port and
+ // set the port handle in the PEB.
+ //
+
+ Status = PsxInitializeSessionPort((ULONG) PebPsxData->SessionPortHandle);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PsxConnectToServer: PsxInitSessionPort failed\n"));
+ return Status;
+ }
+ return STATUS_SUCCESS;
+}
+
+
+//
+// User mode process entry point.
+//
+
+VOID
+PdxProcessStartup(
+ IN PPEB Peb
+ )
+
+{
+ PPEB_PSX_DATA PebPsxData;
+ PFNPROCESS StartAddress;
+ int ReturnCodeFromMain;
+
+ PebPsxData = (PPEB_PSX_DATA)Peb->SubSystemData;
+ StartAddress = (PFNPROCESS)(PebPsxData->ClientStartAddress);
+
+ ReturnCodeFromMain = (*StartAddress) (0, NULL);
+
+ _exit(ReturnCodeFromMain);
+
+ NtTerminateProcess(NtCurrentProcess(),STATUS_ACCESS_DENIED);
+
+}
+
+
+VOID
+PdxNullApiCaller(
+ IN PCONTEXT Context
+ )
+{
+ PdxNullPosixApi();
+#ifdef _X86_
+ Context->Eax = 0;
+#endif
+ NtContinue(Context,FALSE);
+ //NOTREACHED
+}
+
+
+VOID
+PdxSignalDeliverer (
+ IN PCONTEXT Context,
+ IN sigset_t PreviousBlockMask,
+ IN int Signal,
+ IN _handler Handler
+ )
+{
+ (Handler)(Signal);
+ sigprocmask(SIG_SETMASK, &PreviousBlockMask, NULL);
+
+
+#ifdef _X86_
+ Context->Eax = 0;
+#endif
+ NtContinue(Context, FALSE);
+ //NOTREACHED
+}
+
+VOID
+__PdxInitializeData(
+ IN int *perrno,
+ IN char ***penviron
+ )
+/*++
+
+Routine Description:
+
+ This function is called from the RTL startup code to notify the DLL of
+ the location of the variable 'errno'. Necessary because DLLs cannot
+ export data.
+
+Arguments:
+
+ perrno - Supplies the address of errno - declared in rtl/startup.c
+
+Return Value:
+ None.
+
+--*/
+{
+ Errno = perrno;
+ Environ = penviron;
+}
diff --git a/private/posix/client/dllio.c b/private/posix/client/dllio.c
new file mode 100644
index 000000000..18eba2440
--- /dev/null
+++ b/private/posix/client/dllio.c
@@ -0,0 +1,694 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dllio.c
+
+Abstract:
+
+ Client implementation of Input and Output Primitives for POSIX
+
+Author:
+
+ Mark Lucovsky 21-Feb-1989
+
+Revision History:
+
+--*/
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include "psxdll.h"
+
+int
+_CRTAPI1
+close(int fildes)
+{
+ PSX_API_MSG m;
+ NTSTATUS st;
+ PPSX_CLOSE_MSG args;
+
+ args = &m.u.Close;
+ PSX_FORMAT_API_MSG(m, PsxCloseApi, sizeof(*args));
+
+ args->FileDes = fildes;
+
+ st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(st));
+#endif
+
+ if (m.Error) {
+ errno = (int)m.Error;
+ return -1;
+ }
+ return m.ReturnValue;
+}
+
+int
+_CRTAPI1
+creat(const char *path, mode_t mode)
+{
+ return open(path, O_CREAT | O_WRONLY | O_TRUNC, mode);
+}
+
+off_t
+_CRTAPI1
+lseek(int fildes, off_t offset, int whence)
+{
+ PSX_API_MSG m;
+ NTSTATUS st;
+ PPSX_LSEEK_MSG args;
+
+ args = &m.u.Lseek;
+ PSX_FORMAT_API_MSG(m, PsxLseekApi, sizeof(*args));
+
+ args->FileDes = fildes;
+ args->Whence = whence;
+ args->Offset = offset;
+
+ st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(st));
+#endif
+
+ if (m.Error) {
+ errno = (int)m.Error;
+ return -1;
+ }
+ return args->Offset;
+}
+
+int
+_CRTAPI2
+open(const char *path, int oflag, ...)
+{
+ PSX_API_MSG m;
+ NTSTATUS st;
+ PPSX_OPEN_MSG args;
+ int i;
+
+ va_list va_arg;
+
+ va_start(va_arg, oflag);
+
+ args = &m.u.Open;
+ PSX_FORMAT_API_MSG(m, PsxOpenApi, sizeof(*args));
+
+ args->Flags = oflag;
+
+ if (oflag & O_CREAT) {
+
+ //
+ // Create requires a third parameter of type mode_t
+ // which supplies the mode for a file being created
+ //
+
+ args->Mode = va_arg(va_arg, mode_t);
+ }
+
+ va_end(va_arg);
+
+ if (!PdxCanonicalize((PSZ)path, &args->Path_U, PdxPortHeap)) {
+ return -1;
+ }
+
+ ASSERT(NULL != wcschr(args->Path_U.Buffer, L'\\'));
+
+ m.DataBlock = args->Path_U.Buffer;
+ args->Path_U.Buffer = (PWSTR)((PCHAR)m.DataBlock +
+ PsxPortMemoryRemoteDelta);
+
+ for (;;) {
+ st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(st));
+#endif
+
+ if (EINTR == m.Error && SIGCONT == m.Signal) {
+ //
+ // The syscall was stopped and continued. Call again
+ // instead of returning EINTR.
+ //
+
+ PSX_FORMAT_API_MSG(m, PsxOpenApi, sizeof(*args));
+ continue;
+ }
+ if (m.Error) {
+ args->Path_U.Buffer = m.DataBlock;
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)args->Path_U.Buffer);
+ errno = (int)m.Error;
+ return -1;
+ }
+
+ // successful return
+ break;
+ }
+
+ args->Path_U.Buffer = m.DataBlock;
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)args->Path_U.Buffer);
+
+ return m.ReturnValue;
+}
+
+int
+_CRTAPI1
+pipe(int *fildes)
+{
+ PSX_API_MSG m;
+ NTSTATUS st;
+ PPSX_PIPE_MSG args;
+
+ args = &m.u.Pipe;
+ PSX_FORMAT_API_MSG(m, PsxPipeApi, sizeof(*args));
+
+ st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(st));
+#endif
+
+ if (m.Error) {
+ errno = (int)m.Error;
+ return -1;
+ }
+
+ try {
+ fildes[0] = args->FileDes0;
+ fildes[1] = args->FileDes1;
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ st = STATUS_UNSUCCESSFUL;
+ }
+ if (!NT_SUCCESS(st)) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+_CRTAPI1
+read(int fildes, void *buf, unsigned int nbyte)
+{
+ PSX_API_MSG m;
+ PPSX_READ_MSG args;
+ NTSTATUS Status;
+ PVOID SesBuf;
+ SCREQUESTMSG Request;
+ int flags;
+
+ args = &m.u.Read;
+
+ PSX_FORMAT_API_MSG(m, PsxReadApi, sizeof(*args));
+
+ for (;;) {
+ args->FileDes = fildes;
+ args->Buf = buf;
+ args->Nbytes = nbyte;
+ args->Command = IO_COMMAND_DONE;
+
+ Status = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(Status));
+#endif
+
+ if (EINTR == m.Error && SIGCONT == m.Signal) {
+ //
+ // The system call was stopped and continued. Call again
+ // instead of returning EINTR.
+ //
+ PSX_FORMAT_API_MSG(m, PsxReadApi, sizeof(*args));
+ continue;
+ }
+ if (m.Error) {
+ errno = (int)m.Error;
+ return -1;
+ }
+ break;
+ }
+ if (IO_COMMAND_DONE == args->Command) {
+ return m.ReturnValue;
+ }
+
+ ASSERT(IO_COMMAND_DO_CONSIO == args->Command);
+
+ flags = args->Scratch1; // do nonblocking io?
+
+ //
+ // The server says we should read data from the console.
+ //
+
+ if (nbyte > PSX_CON_PORT_DATA_SIZE) {
+ nbyte = PSX_CON_PORT_DATA_SIZE;
+ }
+ SesBuf = ((PPEB_PSX_DATA)NtCurrentPeb()->SubSystemData)->SessionDataBaseAddress;
+ Request.Request = ConRequest;
+ Request.d.Con.Request = ScReadFile;
+ Request.d.Con.d.IoBuf.Handle = (HANDLE)args->FileDes;
+ Request.d.Con.d.IoBuf.Len = nbyte;
+
+ if (flags & O_NONBLOCK) {
+ Request.d.Con.d.IoBuf.Flags = PSXSES_NONBLOCK;
+ } else {
+ Request.d.Con.d.IoBuf.Flags = 0;
+ }
+
+ Status = SendConsoleRequest(&Request);
+
+ //
+ // Want to handle any signals generated as a result of console
+ // operations.
+ //
+
+ PdxNullPosixApi();
+
+ if (0 != Status) {
+ errno = Status;
+ return -1;
+ }
+
+ nbyte = Request.d.Con.d.IoBuf.Len;
+ if (-1 == nbyte) {
+ KdPrint(("PSXDLL: Didn't expect to get here\n"));
+ errno = EINTR;
+ return -1;
+ }
+
+ memcpy(buf, SesBuf, nbyte);
+ return nbyte;
+}
+
+
+ssize_t
+_CRTAPI1
+write(int fildes, const void *buf, size_t nbyte)
+{
+ PSX_API_MSG m;
+ PPSX_WRITE_MSG args;
+ NTSTATUS Status;
+ PVOID SesBuf;
+ SCREQUESTMSG Request;
+ int flags;
+
+ args = &m.u.Write;
+
+ PSX_FORMAT_API_MSG(m, PsxWriteApi, sizeof(*args));
+
+ args->FileDes = fildes;
+ args->Buf = (void *)buf;
+ args->Nbytes = nbyte;
+ args->Command = IO_COMMAND_DONE;
+
+ for (;;) {
+ Status = NtRequestWaitReplyPort(PsxPortHandle,
+ (PPORT_MESSAGE)&m, (PPORT_MESSAGE)&m);
+ if (!NT_SUCCESS(Status)) {
+#ifdef PSX_MORE_ERRORS
+ KdPrint(("PSXDLL: write: NtRequestWaitReplyPort: 0x%x\n", Status));
+#endif
+ _exit(0);
+ }
+
+ if (m.Error == EINTR && m.Signal == SIGCONT) {
+ //
+ // The system call was stopped and continued. Call
+ // again instead of returning EINTR.
+ //
+ PSX_FORMAT_API_MSG(m, PsxWriteApi, sizeof(*args));
+ continue;
+ }
+ if (m.Error) {
+ errno = (int)m.Error;
+ return -1;
+ }
+ break;
+ }
+ if (IO_COMMAND_DONE == args->Command) {
+ return m.ReturnValue;
+ }
+ ASSERT(IO_COMMAND_DO_CONSIO == args->Command);
+
+ flags = args->Scratch1;
+
+ if (nbyte > PSX_CON_PORT_DATA_SIZE) {
+ nbyte = PSX_CON_PORT_DATA_SIZE;
+ }
+ SesBuf = ((PPEB_PSX_DATA)(NtCurrentPeb()->SubSystemData))->SessionDataBaseAddress;
+ Request.Request = ConRequest;
+ Request.d.Con.Request = ScWriteFile;
+ Request.d.Con.d.IoBuf.Handle = (HANDLE)args->FileDes;
+ Request.d.Con.d.IoBuf.Len = nbyte;
+
+ if (flags & O_NONBLOCK) {
+ Request.d.Con.d.IoBuf.Flags = PSXSES_NONBLOCK;
+ }
+
+ memcpy(SesBuf, buf, nbyte);
+
+ Status = SendConsoleRequest(&Request);
+ if (!NT_SUCCESS(Status)) {
+ errno = PdxStatusToErrno(Status);
+ return -1;
+ }
+
+ //
+ // Want to handle any signals generated as a result of console
+ // operations.
+ //
+
+ PdxNullPosixApi();
+
+ if (-1 == Request.d.Con.d.IoBuf.Len) {
+ errno = EBADF;
+ }
+ return Request.d.Con.d.IoBuf.Len;
+}
+
+int
+_CRTAPI1
+dup(int fildes)
+{
+ return fcntl(fildes, F_DUPFD, 0);
+}
+
+int
+_CRTAPI1
+dup2(int fd, int fd2)
+{
+ PSX_API_MSG m;
+ NTSTATUS st;
+ PPSX_DUP2_MSG args;
+
+ args = &m.u.Dup2;
+ PSX_FORMAT_API_MSG(m, PsxDup2Api, sizeof(*args));
+
+ args->FileDes = fd;
+ args->FileDes2 = fd2;
+
+ st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(st));
+#endif
+
+ if (m.Error) {
+ errno = (int)m.Error;
+ return -1;
+ }
+
+ return (int)m.ReturnValue;
+}
+
+int
+_CRTAPI2
+fcntl(int fildes, int cmd, ...)
+{
+ PSX_API_MSG m;
+ NTSTATUS Status;
+ PPSX_FCNTL_MSG args;
+ struct flock *pf, **ppf, *tpf = NULL;
+ int i;
+
+ va_list optarg;
+
+ va_start(optarg, cmd);
+
+ args = &m.u.Fcntl;
+ PSX_FORMAT_API_MSG(m, PsxFcntlApi, sizeof(*args));
+
+ args->FileDes = fildes;
+ args->Command = cmd;
+
+ switch (cmd) {
+ case F_DUPFD:
+
+ // third arg is type int
+
+ args->u.i = va_arg(optarg, int);
+ va_end(optarg);
+ break;
+
+ case F_GETFD:
+
+ // no third arg
+
+ va_end(optarg);
+ break;
+
+ case F_SETFD:
+
+ // third arg is type int
+
+ args->u.i = va_arg(optarg, int);
+ va_end(optarg);
+ break;
+
+ case F_GETFL:
+
+ // no third arg
+
+ va_end(optarg);
+ break;
+
+ case F_SETFL:
+ // third arg is type int
+
+ args->u.i = va_arg(optarg, int);
+ va_end(optarg);
+ break;
+
+ case F_GETLK:
+ case F_SETLK:
+ case F_SETLKW:
+
+ // third arg is type struct flock*
+
+ pf = va_arg(optarg, struct flock *);
+ va_end(optarg);
+
+ tpf = RtlAllocateHeap(PdxPortHeap, 0, sizeof(struct flock));
+ if (NULL == tpf) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ Status = STATUS_SUCCESS;
+ try {
+ memcpy((PVOID)tpf, (PVOID)pf, sizeof(struct flock));
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ Status = STATUS_UNSUCCESSFUL;
+ }
+ if (!NT_SUCCESS(Status)) {
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)tpf);
+ errno = EFAULT;
+ return -1;
+ }
+
+ args->u.pf = (struct flock *)((PCHAR)tpf + PsxPortMemoryRemoteDelta);
+
+ break;
+#if DBG
+ case 99:
+ // no third arg
+ va_end(optarg);
+ break;
+#endif
+ default:
+ // unknown command
+ va_end(optarg);
+ errno = EINVAL;
+ return -1;
+ }
+
+ for (;;) {
+ Status = NtRequestWaitReplyPort(PsxPortHandle,
+ (PPORT_MESSAGE)&m, (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXDLL: fcntl: NtRequest: 0x%x\n", Status));
+ NtTerminateProcess(NtCurrentProcess(), 1);
+ }
+ ASSERT(NT_SUCCESS(Status));
+#endif
+
+ if (m.Error == EINTR && m.Signal == SIGCONT) {
+ PSX_FORMAT_API_MSG(m, PsxFcntlApi, sizeof(*args));
+ continue;
+ }
+ if (m.Error) {
+ if (NULL != tpf) {
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)tpf);
+ }
+ errno = (int)m.Error;
+ return -1;
+ }
+
+ // successful return
+
+ break;
+ }
+
+ if (NULL != tpf) {
+ // copy the flock back to the caller's address
+
+ if (F_GETLK == cmd) {
+ //
+ // Copy the retrieved lock back into the user's buf.
+ //
+ memcpy((PVOID)pf, (PVOID)tpf, sizeof(struct flock));
+ }
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)tpf);
+ }
+
+ return (int)m.ReturnValue;
+}
+
+int
+_CRTAPI1
+isatty(int fd)
+{
+ PSX_API_MSG m;
+ NTSTATUS Status;
+ PPSX_ISATTY_MSG args;
+ SCREQUESTMSG Request;
+
+ args = &m.u.Isatty;
+ PSX_FORMAT_API_MSG(m, PsxIsattyApi, sizeof(*args));
+
+ args->FileDes = fd;
+ args->Command = IO_COMMAND_DONE;
+
+ Status = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(Status));
+#endif
+
+ if (m.Error) {
+ errno = (int)m.Error;
+ return 0;
+ }
+
+ if (IO_COMMAND_DONE == args->Command) {
+ return m.ReturnValue;
+ }
+ ASSERT(IO_COMMAND_DO_CONSIO == args->Command);
+
+ Request.Request = ConRequest;
+ Request.d.Con.Request = ScIsatty;
+ Request.d.Con.d.IoBuf.Handle = (HANDLE)args->FileDes;
+
+ Status = SendConsoleRequest(&Request);
+ if (!NT_SUCCESS(Status)) {
+ errno = PdxStatusToErrno(Status);
+ return 0;
+ }
+
+ //
+ // When the request returns, Len holds the value we're
+ // supposed to return, 0 or 1, and -1 for error.
+ //
+
+ if (-1 == Request.d.Con.d.IoBuf.Len) {
+ errno = EBADF;
+ return 0;
+ }
+ return Request.d.Con.d.IoBuf.Len;
+}
+
+//
+// isatty2 -- just like isatty, but more permissive. Will return
+// TRUE if fd open on a console window, even if _POSIX_TERM is
+// not set.
+//
+
+int
+_CRTAPI1
+isatty2(int fd)
+{
+ PSX_API_MSG m;
+ NTSTATUS Status;
+ PPSX_ISATTY_MSG args;
+ SCREQUESTMSG Request;
+
+ args = &m.u.Isatty;
+ PSX_FORMAT_API_MSG(m, PsxIsattyApi, sizeof(*args));
+
+ args->FileDes = fd;
+ args->Command = IO_COMMAND_DONE;
+
+ Status = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(Status));
+#endif
+
+ if (m.Error) {
+ errno = (int)m.Error;
+ return 0;
+ }
+
+ if (IO_COMMAND_DONE == args->Command) {
+ return m.ReturnValue;
+ }
+ ASSERT(IO_COMMAND_DO_CONSIO == args->Command);
+
+ Request.Request = ConRequest;
+ Request.d.Con.Request = ScIsatty2;
+ Request.d.Con.d.IoBuf.Handle = (HANDLE)args->FileDes;
+
+ Status = SendConsoleRequest(&Request);
+ if (!NT_SUCCESS(Status)) {
+ errno = PdxStatusToErrno(Status);
+ return 0;
+ }
+
+ //
+ // When the request returns, Len holds the value we're
+ // supposed to return, 0 or 1, and -1 for error.
+ //
+
+ if (-1 == Request.d.Con.d.IoBuf.Len) {
+ errno = EBADF;
+ return 0;
+ }
+ return Request.d.Con.d.IoBuf.Len;
+}
+
+int
+_CRTAPI1
+ftruncate(int fildes, off_t len)
+{
+ PSX_API_MSG m;
+ PPSX_FTRUNCATE_MSG args;
+ NTSTATUS Status;
+
+ args = &m.u.Ftruncate;
+
+ PSX_FORMAT_API_MSG(m, PsxFtruncateApi, sizeof(*args));
+
+ args->FileDes = fildes;
+ args->Length = len;
+
+ Status = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+ if (!NT_SUCCESS(Status)) {
+ return -1;
+ }
+
+ if (m.Error) {
+ errno = (int)m.Error;
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/private/posix/client/dllname.c b/private/posix/client/dllname.c
new file mode 100644
index 000000000..83a562381
--- /dev/null
+++ b/private/posix/client/dllname.c
@@ -0,0 +1,310 @@
+#include "psxdll.h"
+
+#if DBG
+VOID DumpNames(PSZ, PSTRING );
+#endif //DBG
+
+PSTRING
+SetPrefix(
+ char **Source
+ )
+{
+ static STRING SparePrefix; // pointer to this may be returned
+ static char PrefixBuf[512];
+
+ //
+ // Test for leading slash. This tells us whether to start at the root
+ // or at the current working directory.
+ //
+
+ if (!IS_POSIX_PATH_SEPARATOR(*Source)) {
+ // relative pathname
+ return &PdxDirectoryPrefix.NtCurrentWorkingDirectory;
+ }
+
+ if (!IS_POSIX_PATH_SEPARATOR(&(*Source)[1])) {
+ // first char is slash, but second is not. Start at root.
+ return &PdxDirectoryPrefix.PsxRoot;
+ }
+ if (IS_POSIX_PATH_SEPARATOR(&(*Source)[2])) {
+ // first three chars are slashes; interpreted as single slash.
+ return &PdxDirectoryPrefix.PsxRoot;
+ }
+
+ //
+ // The path starts with "//something":
+ // //X/ is \DosDevices\X:\
+ //
+
+ memset(PrefixBuf, 0, sizeof(PrefixBuf));
+ strcpy(PrefixBuf, "\\DosDevices\\");
+
+ strncat(PrefixBuf, &(*Source)[2], 1); // get "X"
+ strcat(PrefixBuf, ":"); // make "X:"
+ *Source += 3;
+
+ SparePrefix.Buffer = PrefixBuf;
+ SparePrefix.Length = strlen(PrefixBuf);
+ SparePrefix.MaximumLength = sizeof(PrefixBuf);
+
+ return &SparePrefix;
+}
+
+BOOLEAN
+PdxCanonicalize(
+ IN PCHAR PathName,
+ OUT PUNICODE_STRING CanonPath_U,
+ IN PVOID Heap
+ )
+
+/*++
+
+Routine Description:
+
+ This function accepts a POSIX pathname and converts it into a
+ Unicode NT pathname.
+
+Arguments:
+
+ PathName - Supplies the POSIX pathname to be translated.
+
+ CanonPath_U - Returns the canonicalized Unicode NT pathname. On a
+ successful call, storage is allocated and the CannonPath_U->Buffer
+ points to the allocated storage.
+
+ Heap - Supplies the heap that should be used to allocate storage from
+ to store the canonicalized pathname.
+
+Return Value:
+
+ TRUE - The pathname was successfully canonicalized. Storage was
+ allocated, and the CanonPath_U string is initialized.
+
+ FALSE - The pathname was not canonicalized. No Storage was
+ allocated, and the CanonPath_U string is not initialized. The
+ 'errno' variable is set appropriately in this case.
+
+
+--*/
+
+{
+ ANSI_STRING AnsiCanonPath;
+ PANSI_STRING CanonPath_A;
+ LONG PathNameLength;
+ char *Source, *Dest, *pch;
+ PSTRING Prefix;
+ ULONG UnicodeLength;
+ NTSTATUS Status;
+
+ CanonPath_A = &AnsiCanonPath;
+ CanonPath_A->Buffer = NULL;
+
+ try {
+ PathNameLength = strlen(PathName);
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ PathNameLength = -1;
+ }
+ if (PathNameLength == -1) {
+ errno = EFAULT;
+ return FALSE;
+ }
+ if (PathNameLength == 0) {
+ errno = ENOENT;
+ return FALSE;
+ }
+ if (PathNameLength > PATH_MAX) {
+ errno = ENAMETOOLONG;
+ return FALSE;
+ }
+
+ Source = PathName;
+
+ Prefix = SetPrefix(&Source);
+
+ CanonPath_A->MaximumLength = (USHORT)(PathNameLength + Prefix->Length + 1);
+ CanonPath_A->Buffer = RtlAllocateHeap(Heap, 0, CanonPath_A->MaximumLength);
+ if (NULL == CanonPath_A->Buffer) {
+ errno = ENOMEM;
+ return FALSE;
+ }
+
+ //
+ // Copy the prefix
+ //
+
+ RtlCopyString(CanonPath_A, Prefix);
+
+ Dest = CanonPath_A->Buffer + CanonPath_A->Length;
+
+ while ('\0' != *Source) switch (*Source) {
+ case '/':
+ // Skip adjacent /'s
+
+ if (Dest[-1] != '\\') {
+ *Dest++ = '\\';
+ }
+
+ while (IS_POSIX_PATH_SEPARATOR(Source)) {
+ Source++;
+ }
+ break;
+ case '.':
+ //
+ // Eat single dots as in "/./". For dot-dot back up one level.
+ // Any other dot is just a filename character.
+ //
+
+ if (IS_POSIX_DOT(Source)) {
+ Source++;
+ break;
+ }
+ if (IS_POSIX_DOT_DOT(Source)) {
+ UNICODE_STRING U;
+ OBJECT_ATTRIBUTES Obj;
+ HANDLE FileHandle;
+ IO_STATUS_BLOCK Iosb;
+
+ // back up destination string looking for a \.
+
+ do {
+ Dest--;
+ } while (*Dest != '\\');
+
+ //
+ // Make sure the directory that we're using dot-dot
+ // in actually exists.
+ //
+
+ if (Dest == CanonPath_A->Buffer +
+ PdxDirectoryPrefix.PsxRoot.Length) {
+ *(Dest + 1) = '\000';
+ } else {
+ *Dest = '\000';
+ }
+
+ CanonPath_A->Length = strlen(CanonPath_A->Buffer);
+ Status = RtlAnsiStringToUnicodeString(&U, CanonPath_A,
+ TRUE);
+ if (!NT_SUCCESS(Status)) {
+ RtlFreeHeap(Heap, 0, CanonPath_A->Buffer);
+ errno = ENOMEM;
+ return FALSE;
+ }
+ InitializeObjectAttributes(&Obj, &U, 0, NULL, 0);
+ Status = NtOpenFile(&FileHandle, SYNCHRONIZE, &Obj,
+ &Iosb,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE,
+ FILE_SYNCHRONOUS_IO_NONALERT |
+ FILE_DIRECTORY_FILE);
+ RtlFreeUnicodeString(&U);
+ if (!NT_SUCCESS(Status)) {
+ RtlFreeHeap(Heap, 0, CanonPath_A->Buffer);
+ errno = PdxStatusToErrno(Status);
+ return FALSE;
+ }
+ NtClose(FileHandle);
+
+ //
+ // Back up to previous component: "\a\b\c\" to "\a\b\".
+ // But if we come to the root, we stay there.
+ //
+
+ do {
+ if (Dest == CanonPath_A->Buffer +
+ PdxDirectoryPrefix.PsxRoot.Length) {
+ *Dest++ = '\\';
+ break;
+ }
+ Dest--;
+ } while (*Dest != '\\');
+
+ // Advance source past the dot-dot
+
+ Source += 2;
+ break;
+ }
+
+ // This dot is just a filename character.
+ //FALLTHROUGH
+
+ default:
+ //
+ // Copy a pathname component. If the pathname component
+ // is too long, return ENAMETOOLONG. Note that using a
+ // constant NAME_MAX is bogus, since it could be different
+ // for different filesystems.
+ //
+
+ pch = strchr(Source, '/');
+ if (NULL == pch) {
+ // this is the last component in the path.
+
+ if (strlen(Source) > NAME_MAX) {
+ errno = ENAMETOOLONG;
+ RtlFreeHeap(Heap, 0, CanonPath_A->Buffer);
+ return FALSE;
+ }
+ } else {
+ if (pch - Source > NAME_MAX) {
+ errno = ENAMETOOLONG;
+ RtlFreeHeap(Heap, 0, CanonPath_A->Buffer);
+ return FALSE;
+
+ }
+ }
+
+ while (*Source != '\0' && *Source != '/') {
+ *Dest++ = *Source++;
+ }
+ }
+
+ //
+ // Make sure that we never give back "/DosDevices/C:" ... the
+ // Object Manager doesn't deal with that, we need a trailing
+ // slash.
+ //
+
+ if (Dest == CanonPath_A->Buffer + PdxDirectoryPrefix.PsxRoot.Length) {
+ if (Dest[-1] != '\\') {
+ *Dest++ = '\\';
+ }
+ }
+
+ CanonPath_A->Length = (USHORT)((ULONG)Dest - (ULONG)CanonPath_A->Buffer);
+ CanonPath_A->Buffer[CanonPath_A->Length] = '\0';
+
+ // Convert ansi pathname to unicode - use internal heap for Buffer
+
+ UnicodeLength = RtlAnsiStringToUnicodeSize(CanonPath_A);
+ CanonPath_U->MaximumLength = (USHORT)UnicodeLength;
+ CanonPath_U->Buffer = RtlAllocateHeap(Heap, 0, UnicodeLength);
+
+ if (NULL == CanonPath_U->Buffer) {
+ RtlFreeHeap(Heap, 0, CanonPath_A->Buffer);
+ errno = ENOMEM;
+ return FALSE;
+ }
+
+ Status = RtlAnsiStringToUnicodeString(CanonPath_U, CanonPath_A, FALSE);
+ ASSERT(NT_SUCCESS(Status));
+
+ RtlFreeHeap(Heap, 0, CanonPath_A->Buffer);
+ return TRUE;
+}
+
+#if DBG
+VOID
+DumpNames(
+ IN PSZ PathName,
+ IN PSTRING CanonPath_A
+ )
+{
+ USHORT i;
+ PSZ p;
+
+ KdPrint(("Input Path: \"%s\"\n",PathName));
+ KdPrint(("Output Path: \"%Z\"\n", CanonPath_A));
+}
+
+#endif //DBG
diff --git a/private/posix/client/dllproc.c b/private/posix/client/dllproc.c
new file mode 100644
index 000000000..74b4b8039
--- /dev/null
+++ b/private/posix/client/dllproc.c
@@ -0,0 +1,1251 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dllproc.c
+
+Abstract:
+
+ This module implements POSIX process structure APIs
+
+Author:
+
+ Mark Lucovsky (markl) 27-Jun-1989
+
+Revision History:
+
+--*/
+
+#include <sys\utsname.h>
+#include <unistd.h>
+#include <time.h>
+#include <string.h>
+#include <stdarg.h>
+
+#ifdef _ALPHA_
+#include "psxalpha.h"
+#endif
+#ifdef _MIPS_
+#include "psxmips.h"
+#endif
+#ifdef _PPC_
+#include "psxppc.h"
+#endif
+#ifdef _X86_
+#include "psxi386.h"
+#endif
+
+#include "psxdll.h"
+
+void
+_CRTAPI1
+_exit(int status)
+{
+ PSX_API_MSG m;
+ PPSX_EXIT_MSG args;
+ NTSTATUS st;
+
+ args = &m.u.Exit;
+
+ PSX_FORMAT_API_MSG(m, PsxExitApi, sizeof(*args));
+
+ args->ExitStatus = (ULONG)status;
+
+ st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+
+#ifdef PSX_MORE_ERRORS
+ if (!NT_SUCCESS(st)) {
+ KdPrint(("PSXDLL: _exit: 0x%x\n", st));
+ }
+#endif
+ NtTerminateProcess(NtCurrentProcess(), 0);
+}
+
+gid_t
+_CRTAPI1
+getegid(void)
+{
+ PSX_API_MSG m;
+ PPSX_GETIDS_MSG args;
+ NTSTATUS st;
+
+ args = &m.u.GetIds;
+
+ PSX_FORMAT_API_MSG(m, PsxGetIdsApi, sizeof(*args));
+
+ st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(st));
+#endif
+ return args->EffectiveGid;
+}
+
+gid_t
+_CRTAPI1
+getgid(void)
+{
+ PSX_API_MSG m;
+ PPSX_GETIDS_MSG args;
+ NTSTATUS st;
+
+ args = &m.u.GetIds;
+
+ PSX_FORMAT_API_MSG(m, PsxGetIdsApi, sizeof(*args));
+
+ st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(st));
+#endif
+ return args->RealGid;
+}
+
+uid_t
+_CRTAPI1
+geteuid(void)
+{
+ PSX_API_MSG m;
+ PPSX_GETIDS_MSG args;
+ NTSTATUS st;
+
+ args = &m.u.GetIds;
+
+ PSX_FORMAT_API_MSG(m, PsxGetIdsApi, sizeof(*args));
+
+ st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(st));
+#endif
+ return args->EffectiveUid;
+}
+
+uid_t
+_CRTAPI1
+getuid(void)
+{
+ PSX_API_MSG m;
+ PPSX_GETIDS_MSG args;
+ NTSTATUS st;
+
+ args = &m.u.GetIds;
+
+ PSX_FORMAT_API_MSG(m, PsxGetIdsApi, sizeof(*args));
+
+ st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(st));
+#endif
+ return args->RealUid;
+}
+
+pid_t
+_CRTAPI1
+getppid(void)
+{
+ PSX_API_MSG m;
+ PPSX_GETIDS_MSG args;
+ NTSTATUS st;
+
+ args = &m.u.GetIds;
+
+ PSX_FORMAT_API_MSG(m, PsxGetIdsApi, sizeof(*args));
+
+ st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(st));
+#endif
+ return args->ParentPid;
+}
+
+pid_t
+_CRTAPI1
+getpid(void)
+{
+ PSX_API_MSG m;
+ PPSX_GETIDS_MSG args;
+ NTSTATUS st;
+
+ args = &m.u.GetIds;
+
+ PSX_FORMAT_API_MSG(m, PsxGetIdsApi, sizeof(*args));
+
+ st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(st));
+#endif
+ return args->Pid;
+}
+
+pid_t
+_CRTAPI1
+getpgrp(void)
+{
+ PSX_API_MSG m;
+ PPSX_GETIDS_MSG args;
+ NTSTATUS st;
+
+ args = &m.u.GetIds;
+
+ PSX_FORMAT_API_MSG(m, PsxGetIdsApi, sizeof(*args));
+
+ st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(st));
+#endif
+ return args->GroupId;
+}
+
+pid_t
+_CRTAPI1
+setsid(void)
+{
+ PSX_API_MSG m;
+ NTSTATUS st;
+
+ PSX_FORMAT_API_MSG(m, PsxSetSidApi, 0);
+
+ st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(st));
+#endif
+ if (m.Error) {
+ errno = m.Error;
+ return -1;
+ }
+ return (pid_t)m.ReturnValue;
+}
+
+int
+_CRTAPI1
+setpgid(pid_t pid, pid_t pgid)
+{
+ PSX_API_MSG m;
+ PPSX_SETPGROUPID_MSG args;
+ NTSTATUS st;
+
+ args = &m.u.SetPGroupId;
+
+ PSX_FORMAT_API_MSG(m, PsxSetPGroupIdApi, sizeof(*args));
+
+ args->Pid = pid;
+ args->Pgid = pgid;
+
+ st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(st));
+#endif
+ if (m.Error) {
+ errno = (int)m.Error;
+ return -1;
+ }
+ return (int)m.ReturnValue;
+}
+
+pid_t
+_CRTAPI1
+waitpid(pid_t pid, int *stat_loc, int options)
+{
+ PSX_API_MSG m;
+ PPSX_WAITPID_MSG args;
+ NTSTATUS st;
+
+ args = &m.u.WaitPid;
+
+ PSX_FORMAT_API_MSG(m, PsxWaitPidApi, sizeof(*args));
+
+ args->Pid = pid;
+ args->Options = options;
+
+ for (;;) {
+ st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ if (!NT_SUCCESS(st)) {
+ KdPrint(("PSXDLL: waitpid: 0x%x\n", st));
+ }
+ ASSERT(NT_SUCCESS(st));
+#endif
+
+ if (EINTR == m.Error && SIGCONT == m.Signal) {
+ // We were stopped and then continued. Continue
+ // waiting.
+
+ PSX_FORMAT_API_MSG(m, PsxWaitPidApi, sizeof(*args));
+ continue;
+ }
+
+ if (m.Error) {
+ errno = (int)m.Error;
+ return -1;
+ }
+ if (NULL != stat_loc) {
+ try {
+ *stat_loc = args->StatLocValue;
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ errno = EFAULT;
+ m.ReturnValue = (ULONG)-1;
+ }
+ }
+ return (int)m.ReturnValue;
+ }
+}
+
+pid_t
+_CRTAPI1
+wait(int *stat_loc)
+{
+ PSX_API_MSG m;
+ PPSX_WAITPID_MSG args;
+ NTSTATUS st;
+
+ args = &m.u.WaitPid;
+
+ PSX_FORMAT_API_MSG(m, PsxWaitPidApi, sizeof(*args));
+
+ args->Pid = (pid_t)-1;
+ args->Options = (pid_t)0;
+
+ for (;;) {
+ st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ if (!NT_SUCCESS(st)) {
+ KdPrint(("PSXDLL: wait: NtRequest: 0x%x\n", st));
+ }
+ ASSERT(NT_SUCCESS(st));
+#endif
+
+ if (EINTR == m.Error && SIGCONT == m.Signal) {
+ // We were stopped and continued. Continue waiting.
+ PSX_FORMAT_API_MSG(m, PsxWaitPidApi, sizeof(*args));
+ continue;
+ }
+
+ if (m.Error) {
+ errno = (int)m.Error;
+ return -1;
+ }
+ if (ARGUMENT_PRESENT(stat_loc)) {
+ try {
+ *stat_loc = args->StatLocValue;
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ errno = EFAULT;
+ m.ReturnValue = (ULONG)-1;
+ }
+ }
+ return (int)m.ReturnValue;
+ }
+}
+
+pid_t
+_CRTAPI1
+fork(void)
+{
+ PSX_API_MSG m;
+ NTSTATUS st;
+ PPSX_FORK_MSG args;
+ PTEB ThreadInfo;
+
+ args = &m.u.Fork;
+
+again:
+ PSX_FORMAT_API_MSG(m, PsxForkApi, sizeof(*args));
+
+ ThreadInfo = NtCurrentTeb();
+ args->StackBase = ThreadInfo->NtTib.StackBase;
+ args->StackLimit = ThreadInfo->NtTib.StackLimit;
+ args->StackAllocationBase = ThreadInfo->DeallocationStack;
+
+ try {
+ st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ KdPrint(("PSXDLL: fork: took an exception\n"));
+ KdPrint(("PSXDLL: exception is 0x%x\n", GetExceptionCode()));
+ }
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(st));
+#endif
+
+ if (st == PSX_FORK_RETURN) {
+ st = PsxConnectToServer();
+ if (!NT_SUCCESS(st)) {
+ KdPrint(("PsxConnectToServer: 0x%x\n", st));
+ NtTerminateProcess(NtCurrentProcess(), 1);
+ ASSERT(0);
+ }
+
+ // take any pending signals now.
+ PdxNullPosixApi();
+ return 0;
+ }
+ if (EINTR == m.Error) {
+ // try again.
+ goto again;
+ }
+
+ if (m.Error) {
+ errno = (int)m.Error;
+ return -1;
+ }
+
+ return (int)m.ReturnValue;
+}
+
+//
+// vexec -- Varargs exec program, called by execl*.
+//
+
+int
+vexec(const char *path, const char *arg0, char * const envp[], va_list arglist)
+{
+ NTSTATUS st;
+ PSX_API_MSG m;
+ PPSX_EXEC_MSG args;
+
+ char **ppch;
+ char *pch, *pcharg;
+ int i;
+ int retval = 0;
+ char *Args; // the args + env for the call
+
+ va_list save_arglist;
+
+ try {
+ if (0 == *path) {
+ errno = ENOENT;
+ return -1;
+ }
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ errno = EFAULT;
+ retval = -1;
+ }
+ if (0 != retval) {
+ return retval;
+ }
+
+ args = &m.u.Exec;
+ PSX_FORMAT_API_MSG(m, PsxExecApi, sizeof(*args));
+
+ if (!PdxCanonicalize((PSZ)path, &args->Path, PdxHeap)) {
+ return -1;
+ }
+
+ Args = RtlAllocateHeap(PdxPortHeap, 0, ARG_MAX);
+ if (NULL == Args) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ args->Args = Args + PsxPortMemoryRemoteDelta;
+
+ //
+ // Port Memory Setup is same as for execve, see below.
+ //
+
+ //
+ // first we count the strings so we know how much space to leave
+ // for pointers.
+ //
+
+ save_arglist = arglist;
+
+ for (i = 0, pcharg = va_arg(arglist, char *); NULL != pcharg;
+ pcharg = va_arg(arglist, char *)) {
+ ++i;
+ }
+ ++i; // add one for arg0
+ for (ppch = (char **)envp; NULL != *ppch; ++ppch)
+ ++i;
+ i += 2; // add space for the NULL pointers
+
+ pch = Args + sizeof(char *) * i;
+
+ if (pch > Args + ARG_MAX) {
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)Args);
+ errno = E2BIG;
+ return -1;
+ }
+
+ ppch = (char **)Args;
+
+ arglist = save_arglist; // restart arglist
+
+ try {
+ pcharg = (char *)arg0;
+ while (NULL != pcharg) {
+ if (pch + strlen(pcharg) + 1 > Args + ARG_MAX) {
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)Args);
+ errno = E2BIG;
+ return -1;
+ }
+ *ppch = pch - (ULONG)Args;
+ ppch++;
+ (void)strcpy(pch, pcharg);
+ pcharg = va_arg(arglist, char *);
+
+ pch += strlen(pch);
+ *pch++ = '\0';
+ }
+ *ppch = NULL;
+ ppch++;
+
+ while (NULL != *envp) {
+ if (pch + strlen(*envp) + 1 > Args + ARG_MAX) {
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)Args);
+ errno = E2BIG;
+ return -1;
+ }
+ *ppch = pch - (ULONG)Args;
+ ppch++;
+ (void)strcpy(pch, *envp);
+ envp++;
+
+ pch += strlen(pch);
+ *pch++ = '\0';
+ }
+ *ppch = NULL;
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ errno = EFAULT;
+ retval = -1;
+ }
+ if (0 != retval) {
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)Args);
+ return -1;
+ }
+
+ (void)NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+ //
+ // If we get here, there's been an error.
+ //
+
+ errno = (int)m.Error;
+ RtlFreeHeap(PdxHeap, 0, (PVOID)&args->Path);
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)Args);
+
+ return -1;
+}
+
+int
+_CRTAPI1
+execve(const char *path, char * const argv[], char * const envp[])
+{
+ NTSTATUS st;
+ PSX_API_MSG m;
+ PPSX_EXEC_MSG args;
+ PCHAR Args; // allocate args + environ
+
+ char **ppch;
+ char *pch;
+ int i;
+ int retval = 0;
+
+ try {
+ if (0 == strlen(path)) {
+ errno = ENOENT;
+ return -1;
+ }
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ retval = -1;
+ errno = EFAULT;
+ }
+ if (0 != retval) {
+ return -1;
+ }
+
+ args = &m.u.Exec;
+ PSX_FORMAT_API_MSG(m, PsxExecApi, sizeof(*args));
+
+ if (!PdxCanonicalize((PSZ)path, &args->Path, PdxHeap)) {
+ return -1;
+ }
+
+ //
+ // Copy the caller's environment into view memory so that it may
+ // be transmitted to the "overlaid" process. We set up the port
+ // memory to look like:
+ //
+ // ClientPortMemory:
+ // argv[0]
+ // argv[1]
+ // ...
+ // NULL
+ // envp[0]
+ // envp[1]
+ // ...
+ // NULL
+ // <argv strings>
+ // <environ strings>
+ //
+ // The argv and envp pointers are converted to offsets relative to
+ // ClientPortMemory.
+ //
+ // Because we need all this memory for args and environ, we destroy
+ // the heap and recreate it if the call fails.
+ //
+
+ Args = RtlAllocateHeap(PdxPortHeap, 0, ARG_MAX);
+ if (NULL == Args) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ args->Args = Args + PsxPortMemoryRemoteDelta;
+
+ try {
+
+ // first we count the strings so we know how much space to leave
+ // for pointers.
+
+ for (i = 0, ppch = (char **)argv; NULL != *ppch; ++ppch)
+ ++i;
+ for (ppch = (char **)envp; NULL != *ppch; ++ppch)
+ ++i;
+ i += 2; // add space for the NULL pointers
+
+ pch = Args + sizeof(char *) * i;
+
+ if (pch > Args + ARG_MAX) {
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)Args);
+ errno = E2BIG;
+ return -1;
+ }
+
+ ppch = (char **)Args;
+
+ while (NULL != *argv) {
+ if (pch + strlen(*argv) + 1 > Args + ARG_MAX) {
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)Args);
+ errno = E2BIG;
+ return -1;
+ }
+ *ppch = pch - (ULONG)Args;
+ ppch++;
+ (void)strcpy(pch, *argv);
+ argv++;
+
+ pch += strlen(pch);
+ *pch++ = '\0';
+ }
+ *ppch = NULL;
+ ppch++;
+
+ while (NULL != *envp) {
+ if (pch + strlen(*envp) + 1 > Args + ARG_MAX) {
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)Args);
+ errno = E2BIG;
+ return -1;
+ }
+ *ppch = pch - (ULONG)Args;
+ ppch++;
+ (void)strcpy(pch, *envp);
+ envp++;
+
+ pch += strlen(pch);
+ *pch++ = '\0';
+ }
+ *ppch = NULL;
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ retval = -1;
+ errno = EFAULT;
+ }
+ if (0 != retval) {
+ return -1;
+ }
+
+ (void)NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+ //
+ // If we get here, there's been an error.
+ //
+
+ errno = (int)m.Error;
+ RtlFreeHeap(PdxHeap, 0, (PVOID)&args->Path);
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)Args);
+
+ return -1;
+}
+
+int
+_CRTAPI1
+execv(const char *path, char * const argv[])
+{
+ return execve(path, argv, environ);
+}
+
+int
+_CRTAPI2
+execl(const char *path, const char *arg0, ...)
+{
+ va_list args;
+ int retval;
+
+ va_start(args, arg0);
+
+ retval = vexec(path, arg0, environ, args);
+
+ va_end(args);
+
+ return retval;
+}
+
+int
+_CRTAPI2
+execle(const char *path, const char *arg0, ...)
+{
+ va_list args;
+ char * const *Env;
+ int retval;
+
+ va_start(args, arg0);
+
+ // Skip up to the NULL, then one more, to find environ.
+
+ do {
+ Env = va_arg(args, char * const *);
+ } while (NULL != Env);
+
+ Env = va_arg(args, char * const *);
+
+ va_end(args);
+
+ if (NULL == Env) {
+ return EINVAL;
+ }
+
+ // Restart the arglist traversal
+
+ va_start(args, arg0);
+
+ retval = vexec(path, arg0, Env, args);
+
+ va_end(args);
+
+ return retval;
+}
+
+int
+_CRTAPI2
+execlp(const char *file, const char *arg0, ...)
+{
+ char *pch;
+ char *path;
+ static char buf[PATH_MAX + 1];
+ va_list args;
+ int retval = 0;
+ BOOLEAN done = FALSE;
+
+ va_start(args, arg0);
+
+ //
+ // 1003.1-1990 (3.1.2.2): If the file argument contains a slash
+ // character, the file argument shall be used as the pathname for
+ // this file....
+ //
+
+ try {
+ if ('\0' == *file) {
+ errno = ENOENT;
+ va_end(args);
+ return -1;
+ }
+ if (NULL != (pch = strchr(file, '/'))) {
+ if (-1 == access(file, F_OK)) {
+ va_end(args);
+ return -1;
+ }
+ retval = vexec(file, arg0, environ, args);
+ va_end(args);
+ return retval;
+ }
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ errno = EFAULT;
+ retval = -1;
+ }
+ if (0 != retval) {
+ va_end(args);
+ return -1;
+ }
+
+ //
+ // ... Otherwise, the path prefix for this file is obtained by a
+ // search of the directories passed as the environment variable
+ // PATH.
+ //
+
+ if (NULL == (path = getenv("PATH"))) {
+ //
+ // The file name doesn't contain a slash, and we have
+ // no PATH. We just try for it in the current working
+ // directory, and will return ENOENT if it's not there.
+ //
+ retval = vexec(file, arg0, environ, args);
+ va_end(args);
+ return retval;
+ }
+
+ errno = 0;
+ do {
+ pch = strchr(path, ':');
+ if (NULL == pch) {
+ done = TRUE;
+ } else {
+ *pch = '\0';
+ }
+ if (strlen(path) + strlen(file) + 1 > PATH_MAX) {
+ *pch = ':';
+ errno = ENAMETOOLONG;
+ va_end(args);
+ return -1;
+ }
+ strcpy(buf, path);
+ if (!done) {
+ *pch = ':';
+ path = pch + 1;
+ }
+ if (strlen(buf) > 0) {
+ // this case is "::" in the PATH
+ strcat(buf, "/");
+ }
+ strcat(buf, file);
+
+ // avoid trying to execute files that do not exist.
+
+ if (-1 != access(buf, F_OK)) {
+ (void)vexec(buf, arg0, environ, args);
+ break;
+ }
+ } while (!done);
+
+ va_end(args);
+
+ if (0 == errno) {
+ //
+ // We went all the way through the PATH without finding
+ // a file to exec. Since errno didn't get set by execve(),
+ // we set it here.
+ //
+
+ errno = ENOENT;
+ }
+
+ return -1;
+}
+
+int
+_CRTAPI1
+execvp(const char *file, char * const argv[])
+{
+ char *pch;
+ char *path;
+ static char buf[PATH_MAX + 1];
+ BOOLEAN done = FALSE;
+ int retval = 0;
+
+ //
+ // 1003.1-1990 (3.1.2.2): If the file argument contains a slash
+ // character, the file argument shall be used as the pathname for
+ // this file....
+ //
+
+ try {
+ if ('\0' == *file) {
+ errno = ENOENT;
+ return -1;
+ }
+ if (NULL != (pch = strchr(file, '/'))) {
+ if (-1 == access(file, F_OK)) {
+ return -1;
+ }
+ return execve(file, argv, environ);
+ }
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ errno = EFAULT;
+ retval = -1;
+ }
+ if (0 != retval) {
+ return -1;
+ }
+
+ //
+ // ... Otherwise, the path prefix for this file is obtained by a
+ // search of the directories passed as the environment variable
+ // PATH.
+ //
+
+ if (NULL == (path = getenv("PATH"))) {
+ return execve(file, argv, environ);
+ }
+
+ errno = 0;
+
+ do {
+ pch = strchr(path, ':');
+ if (NULL == pch) {
+ done = TRUE;
+ } else {
+ *pch = '\0';
+ }
+ if (strlen(path) + strlen(file) + 1 > PATH_MAX) {
+ *pch = ':';
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+ strcpy(buf, path);
+ if (!done) {
+ *pch = ':';
+ path = pch + 1;
+ }
+ if (strlen(buf) > 0) {
+ // this case is "::" in the PATH
+ strcat(buf, "/");
+ }
+ strcat(buf, file);
+
+ // avoid trying to execute files that do not exist
+
+ if (-1 != access(buf, F_OK)) {
+ (void)execve(buf, argv, environ);
+ break;
+ }
+ } while (!done);
+
+ if (0 == errno) {
+
+ //
+ // We went all the way through the PATH without finding
+ // a file to exec. Since errno didn't get set by execve(),
+ // we set it here.
+ //
+
+ errno = ENOENT;
+ }
+
+ return -1;
+}
+
+#define COMPUTERNAME_ROOT \
+ L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\ComputerName"
+#define NON_VOLATILE_COMPUTERNAME L"ComputerName"
+#define COMPUTERNAME_VALUE_NAME L"ComputerName"
+#define VALUE_BUFFER_SIZE \
+ (sizeof(KEY_VALUE_PARTIAL_INFORMATION) \
+ + (_POSIX_NAME_MAX * sizeof(WCHAR)))
+
+int _CRTAPI1
+uname(struct utsname *name)
+{
+ NTSTATUS Status;
+ SYSTEM_PROCESSOR_INFORMATION
+ ProcInfo;
+ UNICODE_STRING
+ KeyName,
+ Class,
+ ValueName,
+ Computer_U;
+ ANSI_STRING
+ Computer_A;
+ OBJECT_ATTRIBUTES
+ ObjectAttributes;
+ HANDLE hKey = NULL,
+ hSubKey = NULL;
+ WCHAR ValueBuf[VALUE_BUFFER_SIZE];
+ PKEY_VALUE_PARTIAL_INFORMATION
+ pKeyValueInfo = (PVOID)ValueBuf;
+ ULONG ValueLength;
+ char *pchProcType, // processor type
+ *pchNode = ""; // node name
+ int retval = 0;
+
+ Status = NtQuerySystemInformation(SystemProcessorInformation,
+ (PVOID)&ProcInfo, sizeof(ProcInfo), NULL);
+ if (!NT_SUCCESS(Status)) {
+ errno = PdxStatusToErrno(Status);
+ return -1;
+ }
+
+ switch (ProcInfo.ProcessorArchitecture) {
+ case PROCESSOR_ARCHITECTURE_INTEL:
+ if (ProcInfo.ProcessorLevel == 3) {
+ pchProcType = "i386";
+ } else if (ProcInfo.ProcessorLevel == 4) {
+ pchProcType = "i486";
+ } else if (ProcInfo.ProcessorLevel == 5) {
+ pchProcType = "Pentium";
+ } else {
+ pchProcType = "Intel Unknown";
+ }
+ break;
+
+ case PROCESSOR_ARCHITECTURE_MIPS:
+ pchProcType = "R4000";
+ break;
+
+ case PROCESSOR_ARCHITECTURE_ALPHA:
+ if (ProcInfo.ProcessorLevel == 21064) {
+ pchProcType = "Alpha 21064";
+ } else if (ProcInfo.ProcessorLevel == 21164) {
+ pchProcType = "Alpha 21164";
+ } else {
+ pchProcType = "Alpha Unknown";
+ }
+ break;
+
+ case PROCESSOR_ARCHITECTURE_PPC:
+ if (ProcInfo.ProcessorLevel == 1) {
+ pchProcType = "PowerPC 601";
+ } else if (ProcInfo.ProcessorLevel == 3) {
+ pchProcType = "PowerPC 603";
+ } else if (ProcInfo.ProcessorLevel == 4) {
+ pchProcType = "PowerPC 604";
+ } else if (ProcInfo.ProcessorLevel == 6) {
+ pchProcType = "PowerPC 603+";
+ } else if (ProcInfo.ProcessorLevel == 9) {
+ pchProcType = "PowerPC 604+";
+ } else if (ProcInfo.ProcessorLevel == 20) {
+ pchProcType = "PowerPC 620";
+ } else {
+ pchProcType = "PowerPC Unknown";
+ }
+ break;
+
+ default:
+ pchProcType = "unknown";
+ break;
+ }
+
+ //
+ // Find the node name: this code lifted from
+ // windows/base/client/compname.c
+ //
+
+ RtlInitUnicodeString(&KeyName, COMPUTERNAME_ROOT);
+ InitializeObjectAttributes(&ObjectAttributes, &KeyName,
+ OBJ_CASE_INSENSITIVE, NULL, NULL);
+
+ Status = NtOpenKey(&hKey, KEY_READ, &ObjectAttributes);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXDLL: NtOpenKey: 0x%x\n", Status));
+ goto done;
+ }
+
+ RtlInitUnicodeString(&KeyName, NON_VOLATILE_COMPUTERNAME);
+ InitializeObjectAttributes(&ObjectAttributes, &KeyName,
+ OBJ_CASE_INSENSITIVE, hKey, NULL);
+
+ Status = NtOpenKey(&hSubKey, KEY_READ, &ObjectAttributes);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXDLL: NtOpenKey: 0x%x\n", Status));
+ goto done;
+ }
+
+ RtlInitUnicodeString(&ValueName, COMPUTERNAME_VALUE_NAME);
+
+ Status = NtQueryValueKey(hSubKey, &ValueName,
+ KeyValuePartialInformation,
+ (PVOID)pKeyValueInfo, VALUE_BUFFER_SIZE, &ValueLength);
+ ASSERT(ValueLength < VALUE_BUFFER_SIZE);
+
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXDLL: NtQueryValueKey: 0x%x\n", Status));
+ goto done;
+ }
+ if (pKeyValueInfo->DataLength == 0) {
+ goto done;
+ }
+
+ Computer_U.Buffer = (PVOID)&pKeyValueInfo->Data;
+ Computer_U.Length = Computer_U.MaximumLength =
+ (USHORT)pKeyValueInfo->DataLength;
+
+ Status = RtlUnicodeStringToAnsiString(&Computer_A, &Computer_U, TRUE);
+ if (!NT_SUCCESS(Status)) {
+ goto done;
+ }
+ pchNode = Computer_A.Buffer;
+
+done:
+ if (NULL != hSubKey) {
+ NtClose(hSubKey);
+ }
+ if (NULL != hKey) {
+ NtClose(hKey);
+ }
+
+ try {
+ strncpy((PCHAR)name->sysname, (PCHAR)UNAME_SYSNAME, sizeof(name->sysname));
+ strncpy((PCHAR)name->release, (PCHAR)UNAME_RELEASE, sizeof(name->release));
+ strncpy((PCHAR)name->version, (PCHAR)UNAME_VERSION, sizeof(name->version));
+ strncpy((PCHAR)name->nodename, (PCHAR)pchNode, sizeof(name->nodename));
+ strncpy((PCHAR)name->machine, (PCHAR)pchProcType, sizeof(name->machine));
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ errno = EFAULT;
+ retval = -1;
+ }
+ RtlFreeAnsiString(&Computer_A);
+ return retval;
+}
+
+char * _CRTAPI1
+getenv(const char *name)
+{
+ char **ppch;
+ char *pch;
+
+ try {
+ for (ppch = environ; NULL != *ppch; ++ppch) {
+ if (NULL == (pch = strchr(*ppch, '='))) {
+ continue;
+ }
+ *pch = '\0'; // delete the equals
+ if (0 == strcmp(*ppch, name)) {
+ *pch = '=';
+ return pch + 1;
+ }
+ *pch = '=';
+ }
+ return NULL;
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ errno = EFAULT;
+ }
+ return NULL;
+}
+
+time_t _CRTAPI1
+time(time_t *tloc)
+{
+ LARGE_INTEGER TimeOfDay;
+ ULONG PosixTime;
+
+ NtQuerySystemTime(&TimeOfDay);
+ if (RtlTimeToSecondsSince1970(&TimeOfDay, &PosixTime)) {
+ if (ARGUMENT_PRESENT(tloc)) {
+ try {
+ *tloc = PosixTime;
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ errno = EFAULT;
+ }
+ }
+ } else {
+ PosixTime = (ULONG)-1; // Time not within range of 1970 - 2105
+ }
+ return (time_t)PosixTime;
+}
+
+
+clock_t
+_CRTAPI1
+times(struct tms *buffer)
+{
+ PSX_API_MSG m;
+ PPSX_GETPROCESSTIMES_MSG args;
+ LARGE_INTEGER TimeOfDay;
+ ULONG Remainder;
+ NTSTATUS st;
+ int retval = 0;
+
+ args = &m.u.GetProcessTimes;
+
+ PSX_FORMAT_API_MSG(m, PsxGetProcessTimesApi, sizeof(*args));
+
+ st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(st));
+#endif
+ if (m.Error) {
+ errno = (int)m.Error;
+ return -1;
+ }
+
+ try {
+ *buffer = args->ProcessTimes;
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ errno = EFAULT;
+ retval = -1;
+ }
+ if (0 != retval) {
+ return -1;
+ }
+
+ NtQuerySystemTime(&TimeOfDay);
+
+ TimeOfDay = RtlExtendedLargeIntegerDivide(TimeOfDay, 10000L,
+ &Remainder);
+
+ return TimeOfDay.LowPart;
+}
+
+long
+_CRTAPI1
+sysconf(int name)
+{
+ PSX_API_MSG m;
+ PPSX_SYSCONF_MSG args;
+ NTSTATUS st;
+
+ args = &m.u.Sysconf;
+
+ args->Name = name;
+
+ PSX_FORMAT_API_MSG(m, PsxSysconfApi, sizeof(*args));
+
+ st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(st));
+#endif
+
+ if (0 != m.Error) {
+ errno = m.Error;
+ return -1;
+ }
+ return m.ReturnValue;
+}
+
+int
+_CRTAPI1
+getgroups(int gidsetsize, gid_t *grouplist)
+{
+ PSX_API_MSG m;
+ PPSX_GETGROUPS_MSG args;
+ NTSTATUS st;
+
+ args = &m.u.GetGroups;
+ args->GroupList = grouplist;
+ args->NGroups = gidsetsize;
+
+ //
+ // The Posix server will write group id's into the group
+ // array with NtWriteVirtualMemory, unless gidsetsize is
+ // 0. In that case, he returns the size required and does
+ // not try to write the list.
+ //
+
+ PSX_FORMAT_API_MSG(m, PsxGetGroupsApi, sizeof(*args));
+
+ st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(st));
+#endif
+ if (0 != m.Error) {
+ errno = m.Error;
+ return -1;
+ }
+ return m.ReturnValue;
+}
diff --git a/private/posix/client/dllreg.c b/private/posix/client/dllreg.c
new file mode 100644
index 000000000..3fd5376d8
--- /dev/null
+++ b/private/posix/client/dllreg.c
@@ -0,0 +1,173 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ dllreg.c
+
+Abstract:
+
+ This module implements POSIX registry APIs
+
+Author:
+
+ Matthew Bradburn (mattbr) 13-Dec-1995
+
+Revision History:
+
+--*/
+
+#include <sys\utsname.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdarg.h>
+#include "psxdll.h"
+
+
+//
+// First guess for value size.
+//
+
+#define KEY_WORK_AREA 256
+
+int
+_CRTAPI1
+getreg(char *path, int *type, void *data, size_t *size)
+{
+ NTSTATUS Status;
+ UNICODE_STRING Key_U, Value_U;
+ ANSI_STRING Key_A, Value_A;
+ OBJECT_ATTRIBUTES ObjA;
+ HANDLE hKey = NULL;
+ CHAR *pch;
+ PKEY_VALUE_PARTIAL_INFORMATION pInfo = NULL;
+ UCHAR Buffer[KEY_WORK_AREA];
+ ULONG RequestLength, ResultLength;
+ int r = 0;
+
+ Key_U.Buffer = NULL;
+ Value_U.Buffer = NULL;
+
+ if (strlen(path) > PATH_MAX) {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+
+ //
+ // Split the path into key and value.
+ //
+
+ pch = strrchr(path, '\\');
+ if (NULL == pch) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ Value_A.Buffer = pch + 1;
+ Value_A.Length = strlen(Value_A.Buffer);
+ Value_A.MaximumLength = Value_A.Length + 1;
+
+ Key_A.Buffer = path;
+ Key_A.Length = pch - path;
+ Key_A.MaximumLength = Key_A.Length + 1;
+
+ Status = RtlAnsiStringToUnicodeString(&Key_U, &Key_A, TRUE);
+ if (!NT_SUCCESS(Status)) {
+ errno = PdxStatusToErrno(Status);
+ r = -1;
+ goto out;
+ }
+
+ Status = RtlAnsiStringToUnicodeString(&Value_U, &Value_A, TRUE);
+ if (!NT_SUCCESS(Status)) {
+ errno = PdxStatusToErrno(Status);
+ r = -1;
+ goto out;
+ }
+
+ InitializeObjectAttributes(&ObjA, &Key_U, OBJ_CASE_INSENSITIVE, NULL, NULL);
+
+ Status = NtOpenKey(&hKey, KEY_READ, &ObjA);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXDLL: NtOpenKey: 0x%x\n", Status));
+ errno = PdxStatusToErrno(Status);
+ r = -1;
+ goto out;
+ }
+
+ RequestLength = KEY_WORK_AREA;
+ pInfo = (PKEY_VALUE_PARTIAL_INFORMATION)Buffer;
+
+ for (;;) {
+
+ Status = NtQueryValueKey(hKey, &Value_U, KeyValuePartialInformation,
+ (PVOID)pInfo, RequestLength, &ResultLength);
+
+ if (Status == STATUS_BUFFER_OVERFLOW) {
+
+ //
+ // Try to get a bigger buffer.
+ //
+
+ if (pInfo != (PKEY_VALUE_PARTIAL_INFORMATION)Buffer) {
+
+ RtlFreeHeap(PdxHeap, 0, pInfo);
+ }
+
+ RequestLength += 512;
+ pInfo = (PKEY_VALUE_PARTIAL_INFORMATION)
+ RtlAllocateHeap(PdxHeap, 0, RequestLength);
+
+ if (NULL == pInfo) {
+ errno = ENOMEM;
+ r = -1;
+ goto out;
+ }
+ } else {
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ r = -1;
+ errno = PdxStatusToErrno(Status);
+
+ } else {
+
+ if (pInfo->DataLength > *size) {
+ *size = pInfo->DataLength;
+ *type = 0;
+ errno = E2BIG;
+ r = -1;
+
+ } else {
+
+ *size = pInfo->DataLength;
+ *type = pInfo->Type;
+ memcpy(data, pInfo->Data, pInfo->DataLength);
+ }
+ }
+
+out:
+
+ if (pInfo != NULL && pInfo != (PKEY_VALUE_PARTIAL_INFORMATION)Buffer) {
+ RtlFreeHeap(PdxHeap, 0, pInfo);
+ }
+
+ if (Key_U.Buffer != NULL) {
+ RtlFreeUnicodeString(&Key_U);
+ }
+ if (Value_U.Buffer != NULL) {
+ RtlFreeUnicodeString(&Value_U);
+ }
+ if (hKey != NULL) {
+ NtClose(hKey);
+ }
+ if (pInfo != NULL) {
+ RtlFreeHeap(PdxHeap, 0, (PVOID)pInfo);
+ }
+
+ return r;
+}
diff --git a/private/posix/client/dllsig.c b/private/posix/client/dllsig.c
new file mode 100644
index 000000000..7ab106bfb
--- /dev/null
+++ b/private/posix/client/dllsig.c
@@ -0,0 +1,438 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dllsig.c
+
+Abstract:
+
+ Posix Signal Handling RTL
+
+Author:
+
+ Mark Lucovsky (markl) 10-Mar-1989
+
+Revision History:
+
+--*/
+
+#include "psxdll.h"
+#include <excpt.h>
+
+#define _SIGNULLSET 0x0
+#define _SIGFULLSET 0x7ffff // ((1<<SIGTTOU) - 1)
+
+int
+_CRTAPI1
+sigemptyset(sigset_t *set)
+{
+ int r = 0;
+try {
+ *set = _SIGNULLSET;
+} except (EXCEPTION_EXECUTE_HANDLER) {
+ KdPrint(("PSXDLL: error in sigemptyset\n"));
+ KdPrint(("PSXDLL: set is 0x%x\n", set));
+ KdPrint(("PSXDLL: exception code 0x%x\n", GetExceptionCode()));
+}
+ return r;
+}
+
+int
+_CRTAPI1
+sigfillset(sigset_t *set)
+{
+ int r = 0;
+
+ try {
+ *set = _SIGFULLSET;
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ errno = EFAULT;
+ r = -1;
+ }
+ return r;
+}
+
+int _CRTAPI1
+sigaddset(sigset_t *set, int signo)
+{
+ sigset_t SignoAsMask;
+ int r = 0;
+
+ if (signo < 1 || signo > SIGTTOU) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ SignoAsMask = (ULONG)(1l << (ULONG)(signo-1) );
+
+ try {
+ *set |= SignoAsMask;
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ errno = EFAULT;
+ r = -1;
+ }
+ return r;
+}
+
+int _CRTAPI1
+sigdelset(sigset_t *set, int signo)
+{
+ sigset_t SignoAsMask;
+ int r = 0;
+
+ if (signo < 1 || signo > SIGTTOU) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ SignoAsMask = (ULONG)(1l << (ULONG)(signo-1) );
+
+ try {
+ *set &= ~SignoAsMask;
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ errno = EFAULT;
+ r = -1;
+ }
+ return r;
+}
+
+int _CRTAPI1
+sigismember(const sigset_t *set, int signo)
+{
+ sigset_t SignoAsMask;
+ int r = 0;
+
+ if (signo < 1 || signo > SIGTTOU) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ SignoAsMask = (ULONG)(1L << (ULONG)(signo-1));
+
+ try {
+ if (*set & SignoAsMask) {
+ return 1;
+ }
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ errno = EFAULT;
+ r = -1;
+ }
+ return r;
+}
+
+
+//
+// System Services
+//
+
+int
+_CRTAPI1
+sigaction(int sig, const struct sigaction *act, struct sigaction *oact)
+{
+ PSX_API_MSG m;
+ NTSTATUS st;
+ int r = 0;
+
+ PPSX_SIGACTION_MSG args;
+
+ args = &m.u.SigAction;
+
+ PSX_FORMAT_API_MSG(m, PsxSigActionApi, sizeof(*args));
+
+ args->Sig = (ULONG)sig;
+ args->ActSpecified = (struct sigaction *)act;
+
+ if (ARGUMENT_PRESENT(act)) {
+ try {
+ args->Act = *act;
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ KdPrint(("PSXDLL: err in sigaction\n"));
+ errno = EFAULT;
+ r = -1;
+ }
+ if (r != 0) {
+ return r;
+ }
+ }
+
+ args->OactSpecified = oact;
+
+ st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+
+ if (!NT_SUCCESS(st)) {
+#ifdef PSX_MORE_ERRORS
+ KdPrint(("PSXDLL: sigaction: NtRequestWaitReplyPort: 0x%x\n", st));
+#endif
+ _exit(25);
+ }
+
+ if (m.Error) {
+ errno = (int)m.Error;
+ return -1;
+ } else {
+ if (ARGUMENT_PRESENT(oact)) {
+ try {
+ *oact = args->Oact;
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ errno = EFAULT;
+ r = -1;
+ }
+ if (r != 0) {
+ return r;
+ }
+ }
+ return (int)m.ReturnValue;
+ }
+}
+
+int
+_CRTAPI1
+sigprocmask(int how, const sigset_t *set, sigset_t *oset)
+{
+ PSX_API_MSG m;
+ NTSTATUS st;
+ int r = 0;
+
+ PPSX_SIGPROCMASK_MSG args;
+
+ args = &m.u.SigProcMask;
+
+ PSX_FORMAT_API_MSG(m, PsxSigProcMaskApi, sizeof(*args));
+
+ args->How = (ULONG)how;
+ args->SetSpecified = (sigset_t *)set;
+ if (ARGUMENT_PRESENT(set)) {
+ try {
+ args->Set = *set;
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ r = -1;
+ errno = EFAULT;
+ }
+ if (0 != r) {
+ return r;
+ }
+ }
+
+ st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(st));
+#endif
+
+ if (m.Error) {
+ errno = (int)m.Error;
+ return -1;
+ } else {
+ if (ARGUMENT_PRESENT(oset)) {
+ try {
+ *oset = args->Oset;
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ errno = EFAULT;
+ r = -1;
+ }
+ if (0 != r) {
+ return r;
+ }
+ }
+ return (int)m.ReturnValue;
+ }
+}
+
+int
+_CRTAPI1
+sigsuspend(const sigset_t *sigmask)
+{
+ PSX_API_MSG m;
+ NTSTATUS st;
+
+ PPSX_SIGSUSPEND_MSG args;
+
+ args = &m.u.SigSuspend;
+ PSX_FORMAT_API_MSG(m, PsxSigSuspendApi, sizeof(*args));
+
+ args->SigMaskSpecified = (PVOID)1;
+
+ st = STATUS_SUCCESS;
+
+ try {
+ args->SigMask = *sigmask;
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ st = STATUS_UNSUCCESSFUL;
+ }
+ if (!NT_SUCCESS(st)) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ for (;;) {
+ st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+ if (!NT_SUCCESS(st)) {
+ _exit(26);
+ }
+
+ if (EINTR == m.Error && SIGCONT == m.Signal) {
+ //
+ // We were stopped and continued. Continue
+ // suspending.
+ //
+ PSX_FORMAT_API_MSG(m, PsxSigSuspendApi, sizeof(*args));
+ continue;
+ }
+
+ if (m.Error) {
+ errno = (int)m.Error;
+ return -1;
+ }
+ return (int)m.ReturnValue;
+ }
+}
+
+int
+_CRTAPI1
+pause(void)
+{
+ PSX_API_MSG m;
+ NTSTATUS st;
+
+ PPSX_SIGSUSPEND_MSG args;
+
+ args = &m.u.SigSuspend;
+
+ PSX_FORMAT_API_MSG(m, PsxSigSuspendApi, sizeof(*args));
+
+ args->SigMaskSpecified = NULL;
+
+ for (;;) {
+ st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+ if (!NT_SUCCESS(st)) {
+#ifdef PSX_MORE_ERRORS
+ KdPrint(("PSXDLL: pause: Request: 0x%x\n", st));
+#endif
+ _exit(27);
+ }
+
+ if (EINTR == m.Error && SIGCONT == m.Signal) {
+ //
+ // The syscall was stopped and continues. Call
+ // again instead of returning EINTR.
+ //
+
+ PSX_FORMAT_API_MSG(m, PsxSigSuspendApi, sizeof(*args));
+ continue;
+ }
+ if (m.Error) {
+ errno = (int)m.Error;
+ return -1;
+ }
+ return (int)m.ReturnValue;
+ }
+}
+
+VOID
+PdxNullPosixApi()
+{
+ PSX_API_MSG m;
+ NTSTATUS st;
+
+ PSX_FORMAT_API_MSG(m, PsxNullApi, 0);
+
+ st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ if (!NT_SUCCESS(st)) {
+ KdPrint(("PSXDLL: PdxNullPosixApi: NtRequestWaitReplyPort: 0x%x\n", st));
+ }
+#endif
+}
+
+int
+_CRTAPI1
+kill(pid_t pid, int sig)
+{
+ PSX_API_MSG m;
+ NTSTATUS st;
+
+ PPSX_KILL_MSG args;
+
+ args = &m.u.Kill;
+
+ PSX_FORMAT_API_MSG(m, PsxKillApi, sizeof(*args));
+
+ args->Pid = pid;
+ args->Sig = (ULONG)sig;
+
+ st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+ if (!NT_SUCCESS(st)) {
+#ifdef PSX_MORE_ERRORS
+ KdPrint(("PSXDLL: kill: NtRequestWaitReplyPort: 0x%x\n", st));
+#endif
+ _exit(28);
+ }
+
+ if (m.Error) {
+ errno = (int)m.Error;
+ return -1;
+ } else {
+ return (int)m.ReturnValue;
+ }
+}
+
+#ifndef SIG_ERR
+#define SIG_ERR 0
+#endif
+
+_handler _CRTAPI1
+signal(int sig, _handler handler)
+{
+ struct sigaction act, oact;
+
+ act.sa_handler = handler;
+ act.sa_flags = 0;
+ sigemptyset((sigset_t *)&act.sa_mask);
+
+ if (-1 == sigaction(sig, (struct sigaction *)&act,
+ (struct sigaction *)&oact)) {
+ return SIG_ERR;
+ }
+ return oact.sa_handler;
+}
+
+int
+_CRTAPI1
+sigpending(sigset_t *set)
+{
+ PSX_API_MSG m;
+ PPSX_SIGPENDING_MSG args;
+ NTSTATUS st;
+ int r = 0;
+
+ args = &m.u.SigPending;
+
+ PSX_FORMAT_API_MSG(m, PsxSigPendingApi, sizeof(*args));
+
+ st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(st));
+#endif
+
+ if (m.Error) {
+ errno = (int)m.Error;
+ return -1;
+ }
+
+ try {
+ *set = args->Set;
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ errno = EFAULT;
+ r = -1;
+ }
+ return r;
+}
diff --git a/private/posix/client/dlltc.c b/private/posix/client/dlltc.c
new file mode 100644
index 000000000..6276784f0
--- /dev/null
+++ b/private/posix/client/dlltc.c
@@ -0,0 +1,293 @@
+
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ dlltc.c
+
+Abstract:
+
+ Client implementation of Terminal Control functions for POSIX
+
+Author:
+
+ Ellen Aycock-Wright 05-Aug-1991
+
+Revision History:
+
+--*/
+
+#include <termios.h>
+#include "psxdll.h"
+
+int
+_CRTAPI1
+tcgetattr (int fildes, struct termios *termios_p)
+{
+ PSX_API_MSG m;
+ NTSTATUS st;
+ SCREQUESTMSG Request;
+ PPSX_TCGETATTR_MSG args;
+
+ args = &m.u.TcGetAttr;
+
+ PSX_FORMAT_API_MSG(m,PsxTcGetAttrApi,sizeof(*args));
+
+ args->FileDes = fildes;
+ args->Termios = termios_p;
+
+ st = NtRequestWaitReplyPort(
+ PsxPortHandle,
+ (PPORT_MESSAGE) &m,
+ (PPORT_MESSAGE) &m
+ );
+
+ if ( m.Error ) {
+ errno = (int) m.Error;
+ return (int) -1;
+ }
+
+ //
+ // fildes is a valid file descriptor; now call to posix.exe to get
+ // the real terminal settings.
+ //
+
+ Request.Request = TcRequest;
+ Request.d.Con.Request = TcGetAttr;
+
+ st = SendConsoleRequest(&Request);
+
+ memcpy(termios_p, &Request.d.Tc.Termios, sizeof(*termios_p));
+
+ if (!NT_SUCCESS(st)) {
+ errno = PdxStatusToErrno(st);
+ return -1;
+ }
+ return 0;
+}
+
+int
+_CRTAPI1
+tcsetattr(int fildes, int optional_actions, const struct termios *termios_p)
+{
+ PSX_API_MSG m;
+ NTSTATUS st;
+ SCREQUESTMSG Request;
+ PPSX_TCSETATTR_MSG args;
+
+ args = &m.u.TcSetAttr;
+
+ PSX_FORMAT_API_MSG(m,PsxTcSetAttrApi,sizeof(*args));
+
+ args->FileDes = fildes;
+ args->OptionalActions = optional_actions;
+ args->Termios = (struct termios *)termios_p;
+
+ st = NtRequestWaitReplyPort(
+ PsxPortHandle,
+ (PPORT_MESSAGE) &m,
+ (PPORT_MESSAGE) &m
+ );
+
+ if ( m.Error ) {
+ errno = (int) m.Error;
+ return (int) -1;
+ }
+
+ //
+ // The file descriptor is valid; make the request to posix.exe to
+ // set the attributes.
+ //
+
+ Request.Request = TcRequest;
+ Request.d.Con.Request = TcSetAttr;
+
+ memcpy(&Request.d.Tc.Termios, termios_p, sizeof(*termios_p));
+
+ st = SendConsoleRequest(&Request);
+ if (!NT_SUCCESS(st)) {
+ errno = PdxStatusToErrno(st);
+ return -1;
+ }
+ return 0;
+}
+
+int
+_CRTAPI1
+tcsendbreak (int fildes, int duration)
+{
+ PSX_API_MSG m;
+ NTSTATUS st;
+
+ PPSX_TCSENDBREAK_MSG args;
+
+ args = &m.u.TcSendBreak;
+
+ PSX_FORMAT_API_MSG(m,PsxTcSendBreakApi,sizeof(*args));
+
+ args->FileDes = fildes;
+ args->Duration = duration;
+
+ st = NtRequestWaitReplyPort(
+ PsxPortHandle,
+ (PPORT_MESSAGE) &m,
+ (PPORT_MESSAGE) &m
+ );
+
+ if ( m.Error ) {
+ errno = (int) m.Error;
+ return (int) -1;
+ } else {
+ return (int) m.ReturnValue;
+ }
+}
+
+int
+_CRTAPI1
+tcdrain (int fildes)
+{
+ PSX_API_MSG m;
+ NTSTATUS st;
+
+ PPSX_TCDRAIN_MSG args;
+
+ args = &m.u.TcDrain;
+
+ PSX_FORMAT_API_MSG(m,PsxTcDrainApi,sizeof(*args));
+
+ args->FileDes = fildes;
+
+ st = NtRequestWaitReplyPort(
+ PsxPortHandle,
+ (PPORT_MESSAGE) &m,
+ (PPORT_MESSAGE) &m
+ );
+
+ if ( m.Error ) {
+ errno = (int) m.Error;
+ return (int) -1;
+ } else {
+ return (int) m.ReturnValue;
+ }
+}
+
+int
+_CRTAPI1
+tcflush (int fildes, int queue_selector)
+{
+ PSX_API_MSG m;
+ NTSTATUS st;
+
+ PPSX_TCFLUSH_MSG args;
+
+ args = &m.u.TcFlush;
+
+ PSX_FORMAT_API_MSG(m,PsxTcFlushApi,sizeof(*args));
+
+ args->FileDes = fildes;
+ args->QueueSelector = queue_selector;
+
+ st = NtRequestWaitReplyPort(
+ PsxPortHandle,
+ (PPORT_MESSAGE) &m,
+ (PPORT_MESSAGE) &m
+ );
+
+ if ( m.Error ) {
+ errno = (int) m.Error;
+ return (int) -1;
+ } else {
+ return (int) m.ReturnValue;
+ }
+}
+
+int
+_CRTAPI1
+tcflow (int fildes, int action)
+{
+ PSX_API_MSG m;
+ NTSTATUS st;
+
+ PPSX_TCFLOW_MSG args;
+
+ args = &m.u.TcFlow;
+
+ PSX_FORMAT_API_MSG(m,PsxTcFlowApi,sizeof(*args));
+
+ args->FileDes = fildes;
+ args->Action = action;
+
+ st = NtRequestWaitReplyPort(
+ PsxPortHandle,
+ (PPORT_MESSAGE) &m,
+ (PPORT_MESSAGE) &m
+ );
+
+ if ( m.Error ) {
+ errno = (int) m.Error;
+ return (int) -1;
+ } else {
+ return (int) m.ReturnValue;
+ }
+}
+
+pid_t
+_CRTAPI1
+tcgetpgrp (int fildes)
+{
+ PSX_API_MSG m;
+ NTSTATUS st;
+
+ PPSX_TCGETPGRP_MSG args;
+
+ args = &m.u.TcGetPGrp;
+
+ PSX_FORMAT_API_MSG(m,PsxTcGetPGrpApi,sizeof(*args));
+
+ args->FileDes = fildes;
+
+ st = NtRequestWaitReplyPort(
+ PsxPortHandle,
+ (PPORT_MESSAGE) &m,
+ (PPORT_MESSAGE) &m
+ );
+
+ if ( m.Error ) {
+ errno = (int) m.Error;
+ return (pid_t) -1;
+ } else {
+ return (pid_t) m.ReturnValue;
+ }
+}
+
+int
+_CRTAPI1
+tcsetpgrp(int fildes, pid_t pgrp_id)
+{
+ PSX_API_MSG m;
+ NTSTATUS st;
+
+ PPSX_TCSETPGRP_MSG args;
+
+ args = &m.u.TcSetPGrp;
+
+ PSX_FORMAT_API_MSG(m,PsxTcSetPGrpApi,sizeof(*args));
+
+ args->FileDes = fildes;
+ args->PGrpId = pgrp_id;
+
+ st = NtRequestWaitReplyPort(
+ PsxPortHandle,
+ (PPORT_MESSAGE) &m,
+ (PPORT_MESSAGE) &m
+ );
+
+ if ( m.Error ) {
+ errno = (int) m.Error;
+ return (pid_t) -1;
+ } else {
+ return (pid_t) m.ReturnValue;
+ }
+}
diff --git a/private/posix/client/dlltimer.c b/private/posix/client/dlltimer.c
new file mode 100644
index 000000000..1df993454
--- /dev/null
+++ b/private/posix/client/dlltimer.c
@@ -0,0 +1,243 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dlltimer.c
+
+Abstract:
+
+ This module implements client side stubs for timer related APIs
+
+Author:
+
+ Mark Lucovsky (markl) 08-Aug-1989
+
+Revision History:
+
+--*/
+
+#include "psxdll.h"
+#include <signal.h>
+
+
+ULONG MagicMultiplier = 10000000;
+
+VOID
+SecondsToTime (
+ OUT PLARGE_INTEGER Time,
+ IN ULONG Seconds
+ )
+
+/*++
+
+Routine Description:
+
+ This function converts from seconds to an equivalent
+ relative time value in units of 100ns intervals.
+
+Arguments:
+
+ Time - Returns the equivalant relative time value.
+
+ Seconds - Supplies the time in seconds.
+
+Return Value:
+
+ None.
+
+--*/
+
+
+{
+ Time->QuadPart = (LONGLONG)Seconds * (LONGLONG)MagicMultiplier;
+ Time->QuadPart = -Time->QuadPart;
+}
+
+ULONG
+TimeToSeconds (
+ IN PLARGE_INTEGER Time
+ )
+
+/*++
+
+Routine Description:
+
+ This function converts from absolute time in units of 100ns
+ intervals to seconds.
+
+Arguments:
+
+ Time - Supplies an absolute time in units of 100ns intervals.
+
+Return Value:
+
+ The value of time in seconds.
+
+--*/
+
+{
+ LARGE_INTEGER Seconds;
+
+#if 0
+ Seconds = RtlExtendedMagicDivide(
+ *Time,
+ MagicDivisor,
+ MagicShiftCount
+ );
+
+#else
+ ULONG R; // remainder
+
+ Seconds = RtlExtendedLargeIntegerDivide(
+ *Time, // Dividend
+ MagicMultiplier, // Divisor
+ &R
+ );
+
+#endif
+ return Seconds.LowPart;
+
+}
+
+unsigned int _CRTAPI1
+alarm(unsigned int seconds)
+{
+ PSX_API_MSG m;
+ PPSX_ALARM_MSG args;
+ NTSTATUS st;
+ unsigned int PreviousSeconds;
+
+ args = &m.u.Alarm;
+ PSX_FORMAT_API_MSG(m, PsxAlarmApi, sizeof(*args));
+
+ if (0 == seconds) {
+ args->CancelAlarm = TRUE;
+ } else {
+ SecondsToTime(&args->Seconds, seconds);
+ args->CancelAlarm = FALSE;
+ }
+
+ args->PreviousSeconds.LowPart = 0;
+ args->PreviousSeconds.HighPart = 0;
+
+ st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+
+#ifdef PSX_MORE_ERRORS
+ ASSERT(NT_SUCCESS(st));
+#endif
+
+ PreviousSeconds = TimeToSeconds(&args->PreviousSeconds);
+
+ if (args->PreviousSeconds.LowPart != 0 && PreviousSeconds == 0)
+ PreviousSeconds = 1;
+
+ return PreviousSeconds;
+}
+
+static void
+_CRTAPI1
+sigalrm(int signo)
+{
+ //
+ // XXX.mjb: do we need to do anything here?
+ //
+}
+
+unsigned int
+_CRTAPI1
+sleep(unsigned int seconds)
+{
+ unsigned int prev, t;
+ PVOID ohandler;
+ sigset_t set, oset;
+
+ if (seconds == 0) {
+ getpid(); // encourage context switch
+ return 0;
+ }
+
+ sigemptyset(&set);
+ sigaddset(&set, SIGALRM);
+ sigprocmask(SIG_UNBLOCK, &set, &oset);
+
+ prev = alarm(0);
+
+ if (0 != prev && prev < seconds) {
+ //
+ // There was already an alarm scheduled, and it would
+ // have gone off before the sleep should have been done.
+ // We sleep for the shorter amount of time, and return
+ // with no alarm scheduled.
+ //
+
+ sigset_t s;
+
+ // block SIGALRM until we're ready for it.
+
+ sigemptyset(&s);
+ sigaddset(&s, SIGALRM);
+ sigprocmask(SIG_BLOCK, &s, NULL);
+
+ (void)alarm(prev); // restore previous alarm
+
+ s = oset;
+ sigdelset(&s, SIGALRM);
+ sigsuspend(&s);
+
+ sigprocmask(SIG_SETMASK, &oset, NULL);
+ return seconds - prev;
+ }
+
+
+ ohandler = signal(SIGALRM, sigalrm);
+
+ {
+ sigset_t s;
+
+#if 1
+ // block SIGALARM until we're ready for it.
+
+ sigemptyset(&s);
+ sigaddset(&s, SIGALRM);
+ sigprocmask(SIG_BLOCK, &s, NULL);
+#endif
+
+ (void)alarm(seconds);
+#if 1
+ s = oset;
+ sigdelset(&s, SIGALRM);
+ sigsuspend(&s);
+#else
+ pause();
+#endif
+ }
+
+ t = alarm(0);
+ signal(SIGALRM, ohandler);
+
+ if (0 != prev) {
+ //
+ // There was a previous alarm scheduled, and we re-schedule
+ // it to make it look like we hadn't fiddled with it.
+ //
+
+ if (prev - seconds == 0) {
+ //
+ // the previously-scheduled alarm would have gone
+ // off at the same time the sleep was supposed to
+ // return. We want to make sure two alarms are
+ // actually delivered, so we add a second to the
+ // previous alarm time.
+ //
+ prev++;
+ }
+ (void)alarm(prev - seconds);
+ }
+
+ sigprocmask(SIG_SETMASK, &oset, NULL);
+
+ return t;
+}
diff --git a/private/posix/client/i386/psxthunk.asm b/private/posix/client/i386/psxthunk.asm
new file mode 100644
index 000000000..e31b24177
--- /dev/null
+++ b/private/posix/client/i386/psxthunk.asm
@@ -0,0 +1,60 @@
+ title "PsxSignalThunk"
+;++
+;
+; Copyright (c) 1990 Microsoft Corporation
+;
+; Module Name:
+;
+; psxthunk.asm
+;
+; Abstract:
+;
+; This module implements functions to support Posix signal delivery.
+; Routines in this module are called with non-standard parameter
+; passing.
+;
+; Author:
+;
+; Ellen Aycock-Wright (ellena) 10-Oct-1990
+;
+;
+; Revision History:
+;
+;--
+
+.386p
+ .xlist
+include ks386.inc
+ .list
+
+ extrn _PdxNullApiCaller@4:PROC
+ extrn _PdxSignalDeliverer@16:PROC
+
+
+_TEXT SEGMENT DWORD USE32 PUBLIC 'CODE'
+ ASSUME CS:FLAT, DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+ public __PdxNullApiCaller@4
+__PdxNullApiCaller@4 proc
+
+ mov eax,0
+ call _PdxNullApiCaller@4
+
+; NOTREACHED
+
+__PdxNullApiCaller@4 endp
+
+
+ public __PdxSignalDeliverer@16
+__PdxSignalDeliverer@16 proc
+
+ mov eax,0
+ call _PdxSignalDeliverer@16
+
+; NOTREACHED
+
+
+__PdxSignalDeliverer@16 endp
+
+_TEXT ends
+ end
diff --git a/private/posix/client/i386/sources b/private/posix/client/i386/sources
new file mode 100644
index 000000000..901edcf40
--- /dev/null
+++ b/private/posix/client/i386/sources
@@ -0,0 +1 @@
+i386_SOURCES=psxthunk.asm
diff --git a/private/posix/client/makefile b/private/posix/client/makefile
new file mode 100644
index 000000000..afc6030de
--- /dev/null
+++ b/private/posix/client/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT.
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/posix/client/makefile.inc b/private/posix/client/makefile.inc
new file mode 100644
index 000000000..e0ee8aa44
--- /dev/null
+++ b/private/posix/client/makefile.inc
@@ -0,0 +1,2 @@
+obj\$(TARGET_DIRECTORY)\psxdll.def: psxdll.src
+ $(TARGET_CPP) /EP $(CDEFINES) psxdll.src > obj\$(TARGET_DIRECTORY)\psxdll.def
diff --git a/private/posix/client/mips/psxthunk.s b/private/posix/client/mips/psxthunk.s
new file mode 100644
index 000000000..3dd27910e
--- /dev/null
+++ b/private/posix/client/mips/psxthunk.s
@@ -0,0 +1,147 @@
+// TITLE("POSIX Thunks)
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ psxthunk.s
+
+Abstract:
+
+
+Author:
+
+ Ellena Aycock-Wright (ellena) 11-Jan-1991
+
+Revision History:
+
+--*/
+
+#include "ksmips.h"
+
+
+//++
+//
+// The following code is never executed. Its purpose is to support
+// unwinding through the call to the signal deliverer. (Copied from
+// ntos/rtl/mips/trampoln.s)
+//
+//--
+
+#define FrameSize (ContextFrameLength)
+
+ NESTED_ENTRY(PdxNullApiCall, FrameSize, zero);
+
+ .set noreorder
+ .set noat
+ sd sp,CxXIntSp(sp) // save stack pointer
+ sd ra,CxXIntRa(sp) // save return address
+ sw ra,CxFir(sp) // save return address
+ sd s8,CxXIntS8(sp) // save integer register s8
+ sd gp,CxXIntGp(sp) // save integer register gp
+ sd s0,CxXIntS0(sp) // save integer register s0-s7
+ sd s1,CxXIntS1(sp) //
+ sd s2,CxXIntS2(sp) //
+ sd s3,CxXIntS3(sp) //
+ sd s4,CxXIntS4(sp) //
+ sd s5,CxXIntS5(sp) //
+ sd s6,CxXIntS6(sp) //
+ sd s7,CxXIntS7(sp) //
+ sdc1 f20,CxFltF20(sp) // store floating registers f20 - f31
+ sdc1 f22,CxFltF22(sp) //
+ sdc1 f24,CxFltF24(sp) //
+ sdc1 f26,CxFltF26(sp) //
+ sdc1 f28,CxFltF28(sp) //
+ sdc1 f30,CxFltF30(sp) //
+// move s8,sp // set frame pointer
+ .set at
+ .set reorder
+
+ PROLOGUE_END
+
+ ALTERNATE_ENTRY(_PdxNullApiCaller)
+
+ move a0,sp // set address of context record
+ jal PdxNullApiCaller // call null api caller
+
+ .end PdxNullApiCaller
+
+
+//++
+//
+// The following code is never executed. Its purpose is to support
+// unwinding through the call to the signal deliverer. (Copied from
+// ntos/rtl/mips/trampoln.s)
+//
+//--
+
+ NESTED_ENTRY(PdxSignalDeliver, FrameSize, zero);
+
+ .set noreorder
+ .set noat
+ sd sp,CxXIntSp(sp) // save stack pointer
+ sd ra,CxXIntRa(sp) // save return address
+ sw ra,CxFir(sp) // save return address
+ sd s8,CxXIntS8(sp) // save integer register s8
+ sd gp,CxXIntGp(sp) // save integer register gp
+ sd s0,CxXIntS0(sp) // save integer register s0-s7
+ sd s1,CxXIntS1(sp) //
+ sd s2,CxXIntS2(sp) //
+ sd s3,CxXIntS3(sp) //
+ sd s4,CxXIntS4(sp) //
+ sd s5,CxXIntS5(sp) //
+ sd s6,CxXIntS6(sp) //
+ sd s7,CxXIntS7(sp) //
+ sdc1 f20,CxFltF20(sp) // store floating registers f20 - f31
+ sdc1 f22,CxFltF22(sp) //
+ sdc1 f24,CxFltF24(sp) //
+ sdc1 f26,CxFltF26(sp) //
+ sdc1 f28,CxFltF28(sp) //
+ sdc1 f30,CxFltF30(sp) //
+// move s8,sp // set frame pointer
+ .set at
+ .set reorder
+
+ PROLOGUE_END
+
+//++
+//
+// VOID
+// _PdxSignalDeliverer (
+// IN PCONTEXT Context,
+// IN sigset_t Mask,
+// IN int Signal,
+// IN _handler Handler
+// )
+//
+// Routine Description:
+//
+// The following routine provides linkage to POSIX client routines to perform
+// signal delivery.
+//
+// Arguments:
+//
+// s0 - s7 - Supply parameter values.
+//
+// sp - Supplies the address of a context record.
+//
+// Return Value:
+//
+// There is no return from these routines.
+//
+//--
+
+ ALTERNATE_ENTRY(_PdxSignalDeliverer)
+
+ move a0,sp // set address of context record
+
+ move a1,s1 // set previous block mask
+ move a2,s2 // set signal number
+ move a3,s3 // set signal handler
+
+ jal PdxSignalDeliverer // deliver signal to POSIX client
+
+ //NOTREACHED
+
+ .end _PdxSignalDeliverer
diff --git a/private/posix/client/mips/sources b/private/posix/client/mips/sources
new file mode 100644
index 000000000..3dd005ce9
--- /dev/null
+++ b/private/posix/client/mips/sources
@@ -0,0 +1 @@
+MIPS_SOURCES=psxthunk.asm
diff --git a/private/posix/client/ppc/psxthunk.s b/private/posix/client/ppc/psxthunk.s
new file mode 100644
index 000000000..bb92dbd64
--- /dev/null
+++ b/private/posix/client/ppc/psxthunk.s
@@ -0,0 +1,185 @@
+
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ psxthunk.s
+
+Abstract:
+
+
+Author:
+
+ Ellena Aycock-Wright (ellena) 11-Jan-1991
+
+Revision History:
+
+ Curt Fawcett 9-21-94 Modified for PPC
+
+--*/
+
+#include "ksppc.h"
+ .extern ..PdxNullApiCaller
+ .extern ..PdxSignalDeliverer
+
+
+//++
+//
+// The following code is never executed. Its purpose is to support
+// unwinding through the call to the signal deliverer. (Copied from
+// ntos/rtl/ppc/trampoln.s)
+//
+//--
+
+ FN_TABLE(PdxNullApiCall,0,0)
+
+ DUMMY_ENTRY(PdxNullApiCall)
+
+ mflr r.0
+ stw r.sp,-(ContextFrameLength+STK_MIN_FRAME)(r.sp) // Set frame pointer
+ stw r.sp,(STK_MIN_FRAME+CxGpr1)(r.sp) // Save stack pointer
+ stw r.0,(STK_MIN_FRAME+CxLr)(r.sp) // Save return address
+ stw r.13,(STK_MIN_FRAME+CxGpr13)(r.sp) // Save integer register s8
+ stw r.14,(STK_MIN_FRAME+CxGpr14)(r.sp) // Save integer register gp
+ stw r.15,(STK_MIN_FRAME+CxGpr15)(r.sp) // Save integer registers s0 - s7
+ stw r.16,(STK_MIN_FRAME+CxGpr16)(r.sp) //
+ stw r.17,(STK_MIN_FRAME+CxGpr17)(r.sp) //
+ stw r.18,(STK_MIN_FRAME+CxGpr18)(r.sp) //
+ stw r.19,(STK_MIN_FRAME+CxGpr19)(r.sp) //
+ stw r.20,(STK_MIN_FRAME+CxGpr20)(r.sp) //
+ stw r.21,(STK_MIN_FRAME+CxGpr21)(r.sp) //
+ stw r.22,(STK_MIN_FRAME+CxGpr22)(r.sp) //
+ stw r.23,(STK_MIN_FRAME+CxGpr23)(r.sp) //
+ stw r.24,(STK_MIN_FRAME+CxGpr24)(r.sp) //
+ stw r.25,(STK_MIN_FRAME+CxGpr25)(r.sp) //
+ stw r.26,(STK_MIN_FRAME+CxGpr26)(r.sp) //
+ stw r.27,(STK_MIN_FRAME+CxGpr27)(r.sp) //
+ stw r.28,(STK_MIN_FRAME+CxGpr28)(r.sp) //
+ stw r.29,(STK_MIN_FRAME+CxGpr29)(r.sp) //
+ stw r.30,(STK_MIN_FRAME+CxGpr30)(r.sp) //
+ stw r.31,(STK_MIN_FRAME+CxGpr31)(r.sp) //
+ stfd r.14,(STK_MIN_FRAME+CxFpr14)(r.sp) // Store floating regs f20 - f31
+ stfd r.15,(STK_MIN_FRAME+CxFpr15)(r.sp) //
+ stfd r.16,(STK_MIN_FRAME+CxFpr16)(r.sp) //
+ stfd r.17,(STK_MIN_FRAME+CxFpr17)(r.sp) //
+ stfd r.18,(STK_MIN_FRAME+CxFpr18)(r.sp) //
+ stfd r.19,(STK_MIN_FRAME+CxFpr19)(r.sp) //
+ stfd r.20,(STK_MIN_FRAME+CxFpr20)(r.sp) //
+ stfd r.21,(STK_MIN_FRAME+CxFpr21)(r.sp) //
+ stfd r.22,(STK_MIN_FRAME+CxFpr22)(r.sp) //
+ stfd r.23,(STK_MIN_FRAME+CxFpr23)(r.sp) //
+ stfd r.24,(STK_MIN_FRAME+CxFpr24)(r.sp) //
+ stfd r.25,(STK_MIN_FRAME+CxFpr25)(r.sp) //
+ stfd r.26,(STK_MIN_FRAME+CxFpr26)(r.sp) //
+ stfd r.27,(STK_MIN_FRAME+CxFpr27)(r.sp) //
+ stfd r.28,(STK_MIN_FRAME+CxFpr28)(r.sp) //
+ stfd r.29,(STK_MIN_FRAME+CxFpr29)(r.sp) //
+ stfd r.30,(STK_MIN_FRAME+CxFpr30)(r.sp) //
+ stfd r.31,(STK_MIN_FRAME+CxFpr31)(r.sp) //
+
+ PROLOGUE_END(PdxNullApiCall)
+
+ ALTERNATE_ENTRY(_PdxNullApiCaller)
+
+ mr r.3,r.14
+ bl ..PdxNullApiCaller // call null api caller
+
+ DUMMY_EXIT(PdxNullApiCall)
+
+
+//++
+//
+// The following code is never executed. Its purpose is to support
+// unwinding through the call to the signal deliverer. (Copied from
+// ntos/rtl/ppc/trampoln.s)
+//
+//--
+
+
+ FN_TABLE(PdxSignalDeliver,0,0)
+
+ DUMMY_ENTRY(PdxSignalDeliver)
+
+ mflr r.0
+ stw r.sp,-(ContextFrameLength+STK_MIN_FRAME)(r.sp) // Set frame pointer
+ stw r.sp,(STK_MIN_FRAME+CxGpr1)(r.sp) // Save stack pointer
+ stw r.0,(STK_MIN_FRAME+CxLr)(r.sp) // Save return address
+ stw r.13,(STK_MIN_FRAME+CxGpr13)(r.sp) // Save integer register s8
+ stw r.14,(STK_MIN_FRAME+CxGpr14)(r.sp) // Save integer register gp
+ stw r.15,(STK_MIN_FRAME+CxGpr15)(r.sp) // Save integer registers s0 - s7
+ stw r.16,(STK_MIN_FRAME+CxGpr16)(r.sp) //
+ stw r.17,(STK_MIN_FRAME+CxGpr17)(r.sp) //
+ stw r.18,(STK_MIN_FRAME+CxGpr18)(r.sp) //
+ stw r.19,(STK_MIN_FRAME+CxGpr19)(r.sp) //
+ stw r.20,(STK_MIN_FRAME+CxGpr20)(r.sp) //
+ stw r.21,(STK_MIN_FRAME+CxGpr21)(r.sp) //
+ stw r.22,(STK_MIN_FRAME+CxGpr22)(r.sp) //
+ stw r.23,(STK_MIN_FRAME+CxGpr23)(r.sp) //
+ stw r.24,(STK_MIN_FRAME+CxGpr24)(r.sp) //
+ stw r.25,(STK_MIN_FRAME+CxGpr25)(r.sp) //
+ stw r.26,(STK_MIN_FRAME+CxGpr26)(r.sp) //
+ stw r.27,(STK_MIN_FRAME+CxGpr27)(r.sp) //
+ stw r.28,(STK_MIN_FRAME+CxGpr28)(r.sp) //
+ stw r.29,(STK_MIN_FRAME+CxGpr29)(r.sp) //
+ stw r.30,(STK_MIN_FRAME+CxGpr30)(r.sp) //
+ stw r.31,(STK_MIN_FRAME+CxGpr31)(r.sp) //
+ stfd r.14,(STK_MIN_FRAME+CxFpr14)(r.sp) // Store floating regs f20 - f31
+ stfd r.15,(STK_MIN_FRAME+CxFpr15)(r.sp) //
+ stfd r.16,(STK_MIN_FRAME+CxFpr16)(r.sp) //
+ stfd r.17,(STK_MIN_FRAME+CxFpr17)(r.sp) //
+ stfd r.18,(STK_MIN_FRAME+CxFpr18)(r.sp) //
+ stfd r.19,(STK_MIN_FRAME+CxFpr19)(r.sp) //
+ stfd r.20,(STK_MIN_FRAME+CxFpr20)(r.sp) //
+ stfd r.21,(STK_MIN_FRAME+CxFpr21)(r.sp) //
+ stfd r.22,(STK_MIN_FRAME+CxFpr22)(r.sp) //
+ stfd r.23,(STK_MIN_FRAME+CxFpr23)(r.sp) //
+ stfd r.24,(STK_MIN_FRAME+CxFpr24)(r.sp) //
+ stfd r.25,(STK_MIN_FRAME+CxFpr25)(r.sp) //
+ stfd r.26,(STK_MIN_FRAME+CxFpr26)(r.sp) //
+ stfd r.27,(STK_MIN_FRAME+CxFpr27)(r.sp) //
+ stfd r.28,(STK_MIN_FRAME+CxFpr28)(r.sp) //
+ stfd r.29,(STK_MIN_FRAME+CxFpr29)(r.sp) //
+ stfd r.30,(STK_MIN_FRAME+CxFpr30)(r.sp) //
+ stfd r.31,(STK_MIN_FRAME+CxFpr31)(r.sp) //
+
+ PROLOGUE_END (PdxSignalDeliver)
+
+//++
+//
+// VOID
+// _PdxSignalDeliverer (
+// IN PCONTEXT Context,
+// IN sigset_t Mask,
+// IN int Signal,
+// IN _handler Handler
+// )
+//
+// Routine Description:
+//
+// The following routine provides linkage to POSIX client routines to perform
+// signal delivery.
+//
+// Arguments:
+//
+// r.3 - r.7 - Supply parameter values.
+//
+// sp - Supplies stack frome pointer. Already set up.
+//
+// Return Value:
+//
+// There is no return from these routines.
+//
+//--
+
+ ALTERNATE_ENTRY(_PdxSignalDeliverer)
+
+ mr r.3, r.14 // Set address of context record
+ mr r.4, r.15 // Set previous block mask
+ mr r.5, r.16 // Set signal number
+ mr r.6, r.17 // Set signal handler
+ bl ..PdxSignalDeliverer // deliver signal to POSIX client
+
+ DUMMY_EXIT(PdxSignalDeliver)
+
diff --git a/private/posix/client/ppc/sources b/private/posix/client/ppc/sources
new file mode 100644
index 000000000..fae268538
--- /dev/null
+++ b/private/posix/client/ppc/sources
@@ -0,0 +1 @@
+PPC_SOURCES=psxthunk.s
diff --git a/private/posix/client/psxdll.h b/private/posix/client/psxdll.h
new file mode 100644
index 000000000..59b45d5e3
--- /dev/null
+++ b/private/posix/client/psxdll.h
@@ -0,0 +1,176 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ pdxdll.h
+
+Abstract:
+
+ Posix Subsystem Dll Private Types and Prototypes
+
+Author:
+
+ Mark Lucovsky (markl) 04-Oct-1989
+
+Revision History:
+
+--*/
+
+#ifndef _PDXP_
+#define _PDXP_
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <excpt.h>
+#include <sys\types.h>
+#include <string.h>
+#include <limits.h>
+#include <signal.h>
+#include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys\stat.h>
+#include <sys\wait.h>
+#include <ntsm.h>
+#include <unistd.h>
+#include "psxmsg.h"
+#define NTPSX_ONLY
+#include "sesport.h"
+
+int *Errno;
+#define errno (*Errno)
+
+char ***Environ;
+#define environ (*Environ)
+
+//
+// The PsxDllHandle global variable contains the DLL handle for the WINDLL
+// client stubs executable.
+//
+
+HANDLE PsxDllHandle;
+
+//
+// The connection to the Server is described by the PsxPortHandle global
+// variable. The connection is established when the PsxConnectToServer
+// function is called.
+//
+
+UNICODE_STRING PsxPortName;
+HANDLE PsxPortHandle;
+
+extern ULONG PsxPortMemoryRemoteDelta;
+
+PVOID PdxHeap;
+PVOID PdxPortHeap;
+PSX_DIRECTORY_PREFIX PdxDirectoryPrefix;
+
+ANSI_STRING PsxAnsiCommandLine;
+
+//
+// PathName Conversion Macros
+//
+
+#define IS_POSIX_PATH_SEPARATOR(s) (*(s) == '/' )
+#define IS_POSIX_DOT(s) ( s[0] == '.' && ( s[1] == '/' || s[1] == '\0') )
+#define IS_POSIX_DOT_DOT(s) ( s[0] == '.' && s[1] == '.' && ( s[2] == '/' || s[2] == '\0') )
+#define IN_PORTABLE_CHARACTER_SET(c) (\
+ ((c)>='A' && (c)<='Z') || \
+ ((c)>='a' && (c)<='z') || \
+ ((c)>='0' && (c)<='9') || \
+ ((c)=='.') || \
+ ((c)=='_') || \
+ ((c)=='-') )
+
+typedef int (_CRTAPI1 * PFNPROCESS)(ULONG argc,
+ PCHAR *argv
+ );
+
+//
+// Stuff for the uname() syscall.
+//
+
+#define UNAME_SYSNAME "Windows NT"
+#define UNAME_RELEASE "3"
+#define UNAME_VERSION "5"
+
+//
+// Prototypes
+//
+
+void
+ClientOpen(
+ IN int fd
+ );
+
+VOID
+PdxProcessStartup(
+ IN PPEB Peb
+ );
+
+NTSTATUS
+PsxConnectToServer();
+
+NTSTATUS
+PsxInitDirectories();
+
+VOID
+PdxNullPosixApi();
+
+int
+PdxStatusToErrno(NTSTATUS);
+
+int
+PdxStatusToErrnoPath(PUNICODE_STRING);
+
+VOID
+_PdxSignalDeliverer (
+ IN PCONTEXT Context,
+ IN sigset_t PreviousBlockMask,
+ IN int Signal,
+ IN _handler Handler
+ );
+
+VOID
+PdxSignalDeliverer (
+ IN PCONTEXT Context,
+ IN sigset_t PreviousBlockMask,
+ IN int Signal,
+ IN _handler Handler
+ );
+
+VOID
+_PdxNullApiCaller(
+ IN PCONTEXT Context
+ );
+
+VOID
+PdxNullApiCaller(
+ IN PCONTEXT Context
+ );
+
+
+BOOLEAN
+PdxCanonicalize(
+ IN PSZ PathName,
+ OUT PUNICODE_STRING CanonPath,
+ IN PVOID Heap
+ );
+
+//
+// Routines defined in coninit.c
+//
+
+NTSTATUS PsxInitializeSessionPort(IN ULONG UniqueId);
+
+
+//
+// Routines defined in conrqust.c
+//
+
+NTSTATUS SendConsoleRequest(IN OUT PSCREQUESTMSG Request);
+
+#endif // _PDXP_
diff --git a/private/posix/client/psxdll.rc b/private/posix/client/psxdll.rc
new file mode 100644
index 000000000..80bcfd562
--- /dev/null
+++ b/private/posix/client/psxdll.rc
@@ -0,0 +1,11 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Posix Client DLL"
+#define VER_INTERNALNAME_STR "psxdll.dll"
+
+#include "common.ver"
+
diff --git a/private/posix/client/psxdll.src b/private/posix/client/psxdll.src
new file mode 100644
index 000000000..b99f32012
--- /dev/null
+++ b/private/posix/client/psxdll.src
@@ -0,0 +1,151 @@
+LIBRARY PSXDLL
+
+DESCRIPTION 'POSIX Emulation Subsystem - Client Stubs'
+
+EXPORTS
+ __PdxGetCmdLine
+ __PdxInitializeData
+ fork
+ execl
+ execv
+ execle
+ execve
+ execlp
+ execvp
+ wait
+ waitpid
+ _exit
+ kill
+ signal
+ sigemptyset
+ sigfillset
+ sigaddset
+ sigdelset
+ sigismember
+ sigaction
+ sigprocmask
+ sigpending
+ sigsuspend
+ siglongjmp
+ alarm
+ pause
+ sleep
+ getpid
+ getppid
+ getuid
+ geteuid
+ getgid
+ getegid
+ setuid
+ setgid
+ getgroups
+ getlogin
+ getpgrp
+ getreg
+ setsid
+ setpgid
+ uname
+ time
+ times
+ getenv
+ ctermid
+ ttyname
+ isatty
+ isatty2
+ sysconf
+ opendir
+ readdir
+ rewinddir
+ closedir
+ chdir
+ getcwd
+ open
+ creat
+ umask
+ link
+ mkdir
+ mkfifo
+ unlink
+ rmdir
+ rename
+ stat
+ fstat
+ access
+ chmod
+ chown
+ utime
+ pathconf
+ fpathconf
+ pipe
+ dup
+ dup2
+ close
+ read
+ write
+ fcntl
+ lseek
+ fileno
+ getpwuid
+ getpwnam
+ getgrgid
+ getgrnam
+ tcgetattr
+ tcsetattr
+ tcdrain
+ tcflush
+ tcflow
+ tcsetpgrp
+ tcgetpgrp
+ tcsendbreak
+ cuserid
+ cfgetispeed
+ cfgetospeed
+ cfsetispeed
+ cfsetospeed
+ raise
+ system
+ remove
+ _sigjmp_store_mask
+
+ ;; this for libc, but can't be forwarded
+
+ GetProcessHeap
+
+ ;; apis forwarded for libc
+
+ HeapAlloc = NTDLL.RtlAllocateHeap
+ HeapFree = NTDLL.RtlFreeHeap
+ HeapReAlloc = NTDLL.RtlReAllocateHeap
+ HeapSize = NTDLL.RtlSizeHeap
+ RtlUnwind = NTDLL.RtlUnwind
+ RtlMoveMemory = NTDLL.RtlMoveMemory
+ RtlZeroMemory = NTDLL.RtlZeroMemory
+ RtlFillMemory = NTDLL.RtlFillMemory
+
+ RtlAnsiCharToUnicodeChar = NTDLL.RtlAnsiCharToUnicodeChar
+ RtlMultiByteToUnicodeN = NTDLL.RtlMultiByteToUnicodeN
+ RtlUpcaseUnicodeToMultiByteN = NTDLL.RtlUpcaseUnicodeToMultiByteN
+ RtlUpcaseUnicodeChar = NTDLL.RtlUpcaseUnicodeChar
+ RtlUnicodeToMultiByteN = NTDLL.RtlUnicodeToMultiByteN
+ RtlUnicodeToMultiByteSize = NTDLL.RtlUnicodeToMultiByteSize
+
+#if defined(MIPS)
+ RtlCaptureContext = NTDLL.RtlCaptureContext
+ RtlLookupFunctionEntry = NTDLL.RtlLookupFunctionEntry
+ RtlVirtualUnwind = NTDLL.RtlVirtualUnwind
+#endif
+
+#if defined(ALPHA)
+ RtlCaptureContext = NTDLL.RtlCaptureContext
+ RtlLookupFunctionEntry = NTDLL.RtlLookupFunctionEntry
+ RtlUnwindRfp = NTDLL.RtlUnwindRfp
+ RtlVirtualUnwind = NTDLL.RtlVirtualUnwind
+#endif
+
+#if defined(PPC)
+ RtlCaptureContext = NTDLL.RtlCaptureContext
+ RtlLookupFunctionEntry = NTDLL.RtlLookupFunctionEntry
+ RtlVirtualUnwind = NTDLL.RtlVirtualUnwind
+#endif
+
+ ftruncate
diff --git a/private/posix/client/sources b/private/posix/client/sources
new file mode 100644
index 000000000..232d54fb0
--- /dev/null
+++ b/private/posix/client/sources
@@ -0,0 +1,64 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+WIMPYMASM=1
+
+MAJORCOMP=posix
+MINORCOMP=client
+
+TARGETNAME=psxdll
+TARGETPATH=\nt\public\sdk\lib
+TARGETTYPE=DYNLINK
+DLLENTRY=PsxDllInitialize
+
+LINKLIBS= \
+ \nt\public\sdk\lib\*\ntdll.lib
+
+
+INCLUDES=..\inc;\nt\public\sdk\inc\posix
+
+SOURCES= \
+ coninit.c \
+ conreqst.c \
+ crtsup.c \
+ dllext.c \
+ dllfile.c \
+ dllinit.c \
+ dllio.c \
+ dllname.c \
+ dllproc.c \
+ dllsig.c \
+ dlltc.c \
+ dlltimer.c \
+ dllreg.c \
+ psxdll.rc \
+ stubs.c \
+ sysdb.c
+
+C_DEFINES=-DPSX_IN_WIN -D_POSIX_
+UMTYPE=posix
+UMBASE=0xa00000
+
+NTTARGETFILE0=obj\*\psxdll.def
+DLLDEF=$(NTTARGETFILE0)
diff --git a/private/posix/client/stubs.c b/private/posix/client/stubs.c
new file mode 100644
index 000000000..b21583de7
--- /dev/null
+++ b/private/posix/client/stubs.c
@@ -0,0 +1,195 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ stubs.c
+
+Abstract:
+
+ TEMPORARY - stubs for unimplemented functions
+
+Author:
+
+ Ellen Aycock-Wright (ellena) 15-Jul-1991
+
+Revision History:
+
+--*/
+
+#include <unistd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include <setjmpex.h>
+#include <termios.h>
+#include <sys/stat.h>
+#include "psxdll.h"
+
+char *
+_CRTAPI1
+ctermid(char *s)
+{
+ static char returnspace[L_ctermid];
+
+ if (NULL == s) {
+ return strcpy(returnspace, "");
+ }
+ return strcpy(s, "");
+}
+
+int
+_CRTAPI1
+setgid(gid_t gid)
+{
+ if (gid == getgid()) {
+ return 0;
+ }
+ errno = EPERM;
+ return -1;
+}
+
+int
+_CRTAPI1
+setuid(uid_t uid)
+{
+ if (uid == getuid()) {
+ return 0;
+ }
+ errno = EPERM;
+ return -1;
+}
+
+char *
+_CRTAPI1
+ttyname(int fildes)
+{
+ static char s[7];
+ s[0] = 0;
+ return NULL;
+}
+
+_JBTYPE * _CRTAPI1
+_sigjmp_store_mask(sigjmp_buf env, int save)
+{
+ if (save) {
+ env[_JBLEN] = 1;
+ sigprocmask(SIG_SETMASK, 0, (void *)&env[_JBLEN + 1]);
+ } else {
+ env[_JBLEN] = 0;
+ }
+ return env;
+}
+
+
+void
+_CRTAPI1
+siglongjmp(sigjmp_buf j, int val)
+{
+ if (j[_JBLEN]) {
+ (void)sigprocmask(SIG_SETMASK, (PVOID)&j[_JBLEN + 1], NULL);
+ }
+ longjmp((void *)&j[0], val);
+
+ //NOTREACHED
+}
+
+int
+_CRTAPI1
+cfsetispeed(struct termios *termios_p, speed_t speed)
+{
+ termios_p->c_ispeed = speed;
+ return 0;
+}
+
+int
+_CRTAPI1
+cfsetospeed(struct termios *termios_p, speed_t speed)
+{
+ termios_p->c_ospeed = speed;
+ return 0;
+}
+
+speed_t
+_CRTAPI1
+cfgetispeed(const struct termios *termios_p)
+{
+ return termios_p->c_ispeed;
+}
+
+speed_t
+_CRTAPI1
+cfgetospeed(const struct termios *termios_p)
+{
+ return termios_p->c_ospeed;
+}
+
+int
+_CRTAPI1
+system(const char *command)
+{
+ pid_t pid;
+ int status;
+ char *shell;
+ sigset_t saveblock;
+ struct sigaction sa, saveintr, savequit;
+
+ if (NULL == command) {
+ return 0;
+ }
+
+ // XXX.mjb: should use an absolute path to sh, if there
+ // was one, instead of $SHELL.
+
+ shell = getenv("SHELL");
+ if (NULL == shell) {
+ return 0;
+ }
+
+ sa.sa_handler = SIG_IGN;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+
+ sigaction(SIGINT, &sa, &saveintr);
+ sigaction(SIGQUIT, &sa, &savequit);
+
+ sigaddset(&sa.sa_mask, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &sa.sa_mask, &saveblock);
+
+ pid = fork();
+ if (-1 == pid) {
+ return -1;
+ }
+ if (0 == pid) {
+ sigaction(SIGINT, &saveintr, NULL);
+ sigaction(SIGQUIT, &savequit, NULL);
+ sigprocmask(SIG_SETMASK, &saveblock, NULL);
+ execl(shell, "sh", "-c", command, NULL);
+ _exit(127);
+ }
+ if (-1 == waitpid(pid, &status, 0))
+ status = -1;
+
+ sigaction(SIGINT, &saveintr, NULL);
+ sigaction(SIGQUIT, &savequit, NULL);
+ sigprocmask(SIG_SETMASK, &saveblock, NULL);
+
+ return status;
+}
+
+int _CRTAPI1
+remove(const char *path)
+{
+ struct stat st_buf;
+
+ if (-1 == stat(path, &st_buf))
+ return unlink(path);
+
+ if (S_ISDIR(st_buf.st_mode)) {
+ return rmdir(path);
+ }
+
+ return unlink(path);
+}
diff --git a/private/posix/client/sysdb.c b/private/posix/client/sysdb.c
new file mode 100644
index 000000000..84dbfc078
--- /dev/null
+++ b/private/posix/client/sysdb.c
@@ -0,0 +1,304 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ sysdb.c
+
+Abstract:
+
+ Client side of system database (password and group) access routines.
+
+Author:
+
+ Matthew Bradburn (mattbr) 04-Mar-1992
+
+Revision History:
+
+--*/
+
+#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+#include <stdio.h>
+#include "psxdll.h"
+
+extern PVOID PsxPortMemoryBase;
+
+static char pwbuf[ARG_MAX];
+static char grbuf[ARG_MAX];
+
+struct passwd *
+_CRTAPI1
+getpwuid(uid_t uid)
+{
+ PSX_API_MSG m;
+ PPSX_GETPWUID_MSG args;
+ NTSTATUS Status;
+ struct passwd *tmpbuf;
+
+ args = &m.u.GetPwUid;
+ PSX_FORMAT_API_MSG(m, PsxGetPwUidApi, sizeof(*args));
+
+ args->Uid = uid;
+ args->PwBuf = RtlAllocateHeap(PdxPortHeap, 0, ARG_MAX);
+ ASSERT(args->PwBuf != NULL);
+ args->PwBuf = (struct passwd *)((PCHAR)args->PwBuf +
+ PsxPortMemoryRemoteDelta);
+
+ Status = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+
+ args->PwBuf = (struct passwd *)((PCHAR)args->PwBuf -
+ PsxPortMemoryRemoteDelta);
+
+ if (0 != m.Error) {
+ RtlFreeHeap(PdxPortHeap, 0, args->PwBuf);
+ return NULL;
+ }
+
+ (void)memcpy(pwbuf, args->PwBuf, args->Length);
+
+ RtlFreeHeap(PdxPortHeap, 0, args->PwBuf);
+
+ tmpbuf = (struct passwd *)pwbuf;
+ tmpbuf->pw_name += (ULONG)tmpbuf;
+ tmpbuf->pw_dir += (ULONG)tmpbuf;
+ tmpbuf->pw_shell += (ULONG)tmpbuf;
+
+ return tmpbuf;
+}
+
+struct passwd *
+_CRTAPI1
+getpwnam(const char *name)
+{
+ PSX_API_MSG m;
+ PPSX_GETPWNAM_MSG args;
+ NTSTATUS Status;
+ struct passwd *tmpbuf;
+
+ args = &m.u.GetPwNam;
+ PSX_FORMAT_API_MSG(m, PsxGetPwNamApi, sizeof(*args));
+
+ if ('\0' == *name) {
+ return NULL;
+ }
+
+ args->Name = (PCHAR)RtlAllocateHeap(PdxPortHeap, 0, ARG_MAX);
+ ASSERT(args->Name != NULL);
+
+ strcpy(args->Name,name);
+ args->Name += PsxPortMemoryRemoteDelta;
+ args->PwBuf = (struct passwd *)(args->Name + strlen(name) + 1);
+
+ //
+ // Make sure buffer is properly aligned.
+ //
+
+ while ((ULONG)args->PwBuf & 0x3)
+ args->PwBuf = (PVOID)((ULONG)args->PwBuf + 1);
+
+
+ Status = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+
+ args->Name = args->Name - PsxPortMemoryRemoteDelta;
+ args->PwBuf = (struct passwd *)((PCHAR)args->PwBuf -
+ PsxPortMemoryRemoteDelta);
+
+ if (0 != m.Error) {
+ RtlFreeHeap(PdxPortHeap, 0, args->Name);
+ return NULL;
+ }
+
+ (void)memcpy(pwbuf, args->PwBuf, args->Length);
+
+ RtlFreeHeap(PdxPortHeap, 0, args->Name);
+
+ tmpbuf = (struct passwd *)pwbuf;
+ tmpbuf->pw_name += (ULONG)tmpbuf;
+ tmpbuf->pw_dir += (ULONG)tmpbuf;
+ tmpbuf->pw_shell += (ULONG)tmpbuf;
+
+ return tmpbuf;
+}
+
+struct group *
+_CRTAPI1
+getgrgid(gid_t gid)
+{
+ PSX_API_MSG m;
+ PPSX_GETGRGID_MSG args;
+ NTSTATUS Status;
+ struct group *tmpbuf;
+ char **ppch;
+
+ args = &m.u.GetGrGid;
+ PSX_FORMAT_API_MSG(m, PsxGetGrGidApi, sizeof(*args));
+ args->Gid = gid;
+
+ args->GrBuf = RtlAllocateHeap(PdxPortHeap, 0, ARG_MAX);
+ if (NULL == args->GrBuf) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ args->GrBuf = (struct group *)((PCHAR)args->GrBuf +
+ PsxPortMemoryRemoteDelta);
+
+ Status = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+
+ args->GrBuf = (struct group *)((PCHAR)args->GrBuf -
+ PsxPortMemoryRemoteDelta);
+
+ if (0 != m.Error) {
+ RtlFreeHeap(PdxPortHeap, 0, args->GrBuf);
+ return NULL;
+ }
+
+ (void)memcpy(grbuf, args->GrBuf, args->Length);
+
+ RtlFreeHeap(PdxPortHeap, 0, args->GrBuf);
+
+ tmpbuf = (void *)grbuf;
+ tmpbuf->gr_name = (PCHAR)((ULONG)grbuf + (ULONG)tmpbuf->gr_name);
+ tmpbuf->gr_mem = (PCHAR *)((ULONG)grbuf + (ULONG)tmpbuf->gr_mem);
+
+ for (ppch = tmpbuf->gr_mem; NULL != *ppch; ++ppch) {
+ *ppch = (PCHAR)((ULONG)grbuf + (ULONG)*ppch);
+ }
+ return tmpbuf;
+}
+
+struct group *
+_CRTAPI1
+getgrnam(const char *name)
+{
+ PSX_API_MSG m;
+ PPSX_GETGRNAM_MSG args;
+ NTSTATUS Status;
+ struct group *tmpbuf;
+ char **ppch;
+
+ args = &m.u.GetGrNam;
+ PSX_FORMAT_API_MSG(m, PsxGetGrNamApi, sizeof(*args));
+
+ if ('\0' == *name) {
+ //
+ // We need to take special care of this case, because
+ // SAM will find a match for the null name.
+ //
+ return NULL;
+ }
+
+ args->Name = (PCHAR)RtlAllocateHeap(PdxPortHeap, 0, ARG_MAX);
+ ASSERT(args->Name != NULL);
+
+ strcpy(args->Name,name);
+ args->Name += PsxPortMemoryRemoteDelta;
+
+ args->GrBuf = (struct group *)(args->Name + strlen(name) + 1);
+
+ //
+ // Make sure buffer is properly aligned.
+ //
+
+ while ((ULONG)args->GrBuf & 0x3)
+ args->GrBuf = (PVOID)((ULONG)args->GrBuf + 1);
+
+ Status = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+
+ args->Name = args->Name - PsxPortMemoryRemoteDelta;
+ args->GrBuf = (struct group *)((PCHAR)args->GrBuf -
+ PsxPortMemoryRemoteDelta);
+
+ if (0 != m.Error) {
+ RtlFreeHeap(PdxPortHeap, 0, args->Name);
+ return NULL;
+ }
+
+ (void)memcpy(grbuf, args->GrBuf, args->Length);
+ tmpbuf = (void *)grbuf;
+ tmpbuf->gr_name = (PCHAR)((ULONG)grbuf + (ULONG)tmpbuf->gr_name);
+ tmpbuf->gr_mem = (PCHAR *)((ULONG)grbuf + (ULONG)tmpbuf->gr_mem);
+
+ for (ppch = tmpbuf->gr_mem; NULL != *ppch; ++ppch) {
+ *ppch = (PCHAR)((ULONG)grbuf + (ULONG)*ppch);
+ }
+ RtlFreeHeap(PdxPortHeap, 0, (PVOID)args->Name);
+ return tmpbuf;
+}
+
+char *
+_CRTAPI1
+getlogin(void)
+{
+ static char name[32];
+ PSX_API_MSG m;
+ PPSX_GETPWUID_MSG args;
+ NTSTATUS Status;
+
+ //
+ // We just do the equivalent of getpwuid(getuid()) and then
+ // throw away everything but the name.
+ //
+
+ //
+ // XXX.mjb: do I need to use a name outside the POSIX namespace
+ // for this? Like, what happens if the user has re-defined getuid()?
+ //
+
+ args = &m.u.GetPwUid;
+ PSX_FORMAT_API_MSG(m, PsxGetPwUidApi, sizeof(*args));
+
+ args->Uid = getuid();
+ args->PwBuf = (struct passwd *)RtlAllocateHeap(PdxPortHeap, 0, ARG_MAX);
+ ASSERT(args->PwBuf != NULL);
+ args->PwBuf = (struct passwd *)((PCHAR)args->PwBuf +
+ PsxPortMemoryRemoteDelta);
+
+ Status = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
+ (PPORT_MESSAGE)&m);
+
+ args->PwBuf = (struct passwd *)((PCHAR)args->PwBuf -
+ PsxPortMemoryRemoteDelta);
+
+ if (0 != m.Error) {
+ RtlFreeHeap(PdxPortHeap, 0, args->PwBuf);
+ return NULL;
+ }
+
+ strcpy(name, (PCHAR)((ULONG)(args->PwBuf->pw_name) + (ULONG)args->PwBuf));
+
+ RtlFreeHeap(PdxPortHeap, 0, args->PwBuf);
+
+ return name;
+}
+
+#ifndef L_cuserid
+#define L_cuserid 32
+#endif
+
+char *
+_CRTAPI1
+cuserid(char *s)
+{
+ static char ReturnSpace[ L_cuserid ];
+ struct passwd *PassWd;
+ char *Dest;
+
+ PassWd = getpwuid( getuid() );
+
+ if( PassWd == NULL ) {
+ *s = '\0';
+ return( ( s == NULL ) ? NULL : s );
+ } else {
+ Dest = ( ( s == NULL ) ? ReturnSpace : s );
+ strncpy( Dest, PassWd->pw_name, L_cuserid - 1 );
+ Dest[ L_cuserid - 1 ] = '\0';
+ }
+}
+
diff --git a/private/posix/client/tst/tstdir.c b/private/posix/client/tst/tstdir.c
new file mode 100644
index 000000000..cac768cd5
--- /dev/null
+++ b/private/posix/client/tst/tstdir.c
@@ -0,0 +1,308 @@
+#include <nt.h>
+#include <ntrtl.h>
+
+#include <string.h>
+#include <signal.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <fcntl.h>
+
+#include "tsttmp.h" // defines DbgPrint as printf
+
+extern int errno;
+VOID dir0(char *);
+VOID dir1(char *);
+VOID dir2(char *);
+VOID dir3(char *);
+VOID dir4(char *);
+VOID dir5(char *);
+
+
+int
+main(int argc, char *argv[])
+{
+
+ DbgPrint("argc = %d, argv[1] = %s\n", argc, argv[1]);
+
+ if (argc != 2) {
+ DbgPrint("Usage: 'tstdir basedirectory' (basedirectory is usually /psx/test)\n");
+ return 1;
+ }
+
+ dir0(argv[1]);
+ dir1(argv[1]);
+ dir2(argv[1]);
+ dir3(argv[1]);
+ dir4(argv[1]);
+ dir5(argv[1]);
+
+ return 1;
+}
+
+
+VOID
+dir0(char *f)
+{
+ int rc;
+ DIR *dir;
+ struct dirent *dirent;
+
+ DbgPrint("dir0:++ %s\n",f);
+
+ dir = opendir(f);
+ ASSERT(dir != NULL);
+
+ errno = 0;
+ dirent = readdir(dir);
+ while( dirent ) {
+ DbgPrint("%s\n",&dirent->d_name);
+ dirent = readdir(dir);
+ }
+ ASSERT(errno == 0);
+
+ rc = closedir(dir);
+ ASSERT(rc==0);
+
+ rc = closedir(dir);
+ ASSERT(rc==-1 && errno == EBADF);
+
+ DbgPrint("dir0:--\n");
+}
+
+VOID
+dir1(char *f)
+{
+ int rc,i;
+ DIR *dir;
+
+ char buf1[256],buf2[256];
+ struct dirent *dirent;
+
+ DbgPrint("dir1:++ %s\n",f);
+
+ dir = opendir(f);
+ ASSERT(dir != NULL);
+
+ //
+ // Get Two Directory entries.
+ //
+ i = 0;
+ errno = 0;
+ dirent = readdir(dir);
+ if ( dirent ) {
+ strcpy(buf1,(char *)&dirent->d_name);
+ i++;
+ }
+ dirent = readdir(dir);
+ if ( dirent ) {
+ strcat(buf1,(char *)&dirent->d_name);
+ i++;
+ }
+
+ //
+ // Go past a few entries
+ //
+
+ dirent = readdir(dir);
+ if ( dirent ) {
+ dirent = readdir(dir);
+ }
+
+ //
+ // Rewind the directory stream and then read into a new buffer
+ // and compare results
+ //
+
+ rewinddir(dir);
+
+ buf2[0]='\0';
+ while(i--) {
+ dirent = readdir(dir);
+ ASSERT(dirent);
+ strcat(buf2,(char *)&dirent->d_name);
+ }
+ ASSERT(strcmp(buf1,buf2) == 0);
+
+ rc = closedir(dir);
+ ASSERT(rc==0);
+
+ rc = closedir(dir);
+ ASSERT(rc==-1 && errno == EBADF);
+
+ DbgPrint("dir1:--\n");
+}
+
+VOID
+dir2(char *f)
+{
+ int rc;
+ char buf[256], *b;
+
+
+ DbgPrint("dir2:++ %s\n",f);
+
+ rc = chdir(f);
+ ASSERT(rc==0);
+
+ b = getcwd(buf,256);
+ ASSERT(b);
+
+ b = getcwd(buf,-1);
+ ASSERT(b==NULL && errno == EINVAL);
+
+ b = getcwd(buf,1);
+ ASSERT(b==NULL && errno == ERANGE);
+
+ rc = chdir("/psx/test/tstdirs");
+ ASSERT(rc==0);
+
+ b = getcwd(buf,256);
+ ASSERT(b);
+
+ b = getcwd(buf,-1);
+ ASSERT(b==NULL && errno == EINVAL);
+
+ b = getcwd(buf,1);
+ ASSERT(b==NULL && errno == ERANGE);
+
+ DbgPrint("dir2:--\n");
+}
+
+VOID
+dir3(char *f)
+{
+ int rc;
+ DIR *dir;
+ struct dirent *dirent;
+
+ DbgPrint("dir3:++ %s\n",f);
+
+ rc = chdir(f);
+ ASSERT(rc==0);
+
+ dir = opendir(".");
+ ASSERT(dir != NULL);
+
+ errno = 0;
+ dirent = readdir(dir);
+ while( dirent ) {
+ DbgPrint("%s\n",&dirent->d_name);
+ dirent = readdir(dir);
+ }
+ ASSERT(errno == 0);
+
+ rc = closedir(dir);
+ ASSERT(rc==0);
+
+ rc = chdir("/psx/test/tstdirs");
+ ASSERT(rc==0);
+
+ dir = opendir(".");
+ ASSERT(dir != NULL);
+
+ errno = 0;
+ dirent = readdir(dir);
+ while( dirent ) {
+ DbgPrint("%s\n",&dirent->d_name);
+ dirent = readdir(dir);
+ }
+ ASSERT(errno == 0);
+
+ rc = closedir(dir);
+ ASSERT(rc==0);
+
+
+ DbgPrint("dir3:--\n");
+}
+
+
+VOID
+dir4(char *f)
+{
+ int rc;
+ DIR *dir;
+ struct dirent *dirent;
+
+ DbgPrint("dir4:++ %s\n",f);
+
+ dir = opendir(f);
+ ASSERT(dir != NULL);
+
+ if ( fork() == 0 ) {
+ sleep(10);
+ errno = 0;
+ dirent = readdir(dir);
+ while( dirent ) {
+ DbgPrint("%s\n",&dirent->d_name);
+ dirent = readdir(dir);
+ }
+ ASSERT(errno == 0);
+
+ rc = closedir(dir);
+ ASSERT(rc==0);
+
+ rc = closedir(dir);
+ ASSERT(rc==-1 && errno == EBADF);
+
+ _exit(0);
+ }
+
+ rc = closedir(dir);
+ ASSERT(rc==0);
+
+ rc = closedir(dir);
+ ASSERT(rc==-1 && errno == EBADF);
+
+ wait(NULL);
+
+ DbgPrint("dir4:--\n");
+}
+
+VOID
+dir5(char *f)
+{
+ int rc;
+ char buf[256], *b;
+ PCH Arg[3], Env[4];
+
+
+ DbgPrint("dir5:++ %s\n",f);
+
+ rc = chdir(f);
+ ASSERT(rc==0);
+
+ b = getcwd(buf,256);
+ ASSERT(b);
+
+ b = getcwd(buf,-1);
+ ASSERT(b==NULL && errno == EINVAL);
+
+ b = getcwd(buf,1);
+ ASSERT(b==NULL && errno == ERANGE);
+
+ rc = chdir("/psx/test/tstdirs");
+ ASSERT(rc==0);
+
+ b = getcwd(buf,256);
+ ASSERT(b);
+
+ b = getcwd(buf,-1);
+ ASSERT(b==NULL && errno == EINVAL);
+
+ b = getcwd(buf,1);
+ ASSERT(b==NULL && errno == ERANGE);
+
+ Arg[0]="tsthello.xxx";
+ Arg[1]=(PCH)NULL;
+ Env[0]="NTUSER=ELLENA";
+ Env[1]=(PCH)NULL;
+
+ execve("\\DosDevices\\D:\\PSX\\TSTHELLO.exe",Arg,Env);
+
+ ASSERT(FALSE);
+
+ DbgPrint("dir5:--\n");
+}
diff --git a/private/posix/client/tst/tstexec.c b/private/posix/client/tst/tstexec.c
new file mode 100644
index 000000000..53c9e53e6
--- /dev/null
+++ b/private/posix/client/tst/tstexec.c
@@ -0,0 +1,68 @@
+#include <nt.h>
+#include <ntrtl.h>
+
+#include <string.h>
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <fcntl.h>
+
+#include "tsttmp.h" // defines DbgPrint as printf
+
+extern int errno;
+VOID dir5(char *);
+
+
+int
+_CRTAPI1
+main(int argc, char *argv[])
+{
+
+ DbgPrint("argc = %d, argv[1] = %s\n", argc, argv[1]);
+
+ if (argc != 2) {
+
+ DbgPrint("Usage: 'tstexec basedirectory' (basedirectory is usually /psx/test)\n");
+ return 1;
+ }
+
+ dir5(argv[1]);
+
+ return 1;
+}
+
+VOID
+dir5(char *f)
+{
+ int rc;
+ char buf[256], *b;
+ PCH Arg[3], Env[4];
+
+ DbgPrint("dir5:++ %s\n",f);
+
+ rc = chdir(f);
+ ASSERT(rc==0);
+
+ b = getcwd(buf,256);
+ ASSERT(b);
+
+ rc = chdir("/psx/test/tstdirs");
+ ASSERT(rc==0);
+
+ b = getcwd(buf,256);
+ ASSERT(b);
+
+ Arg[0]="tsthello.xxx";
+ Arg[1]=(PCH)NULL;
+ Env[0]="NTUSER=ELLENA";
+ Env[1]=(PCH)NULL;
+
+ execve("\\DosDevices\\D:\\PSX\\TSTHELLO.exe",Arg,Env);
+
+ ASSERT(FALSE);
+
+ DbgPrint("dir5:--\n");
+}
diff --git a/private/posix/client/tst/tstfile.c b/private/posix/client/tst/tstfile.c
new file mode 100644
index 000000000..102cd984e
--- /dev/null
+++ b/private/posix/client/tst/tstfile.c
@@ -0,0 +1,252 @@
+//
+// Usage comments:
+// Current working directory default is d:/psx - put test files there
+// Needs tstf.one tstf.two out.dat
+// 'file tstf.one tstf.two'
+//
+
+#include <nt.h>
+#include <ntrtl.h>
+
+#include <signal.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "tsttmp.h" // defines DbgPrint as printf
+
+extern int errno;
+
+VOID file0(char *);
+VOID file1(char *);
+VOID file2(char *, char *);
+VOID file3(char *);
+VOID file4(char *, char *);
+
+
+int
+main(int argc, char *argv[])
+{
+
+
+ if (argc != 3) {
+ DbgPrint("Usage: 'tstfile tstf.one tstf.two'\n");
+ return 1;
+ }
+ file0(argv[1]);
+ file1(argv[1]);
+ file2(argv[1],argv[2]);
+ file3(argv[1]);
+ file4(argv[1],argv[2]);
+
+ return 1;
+}
+
+
+VOID
+file0(char *f)
+{
+ int rc,fd;
+ char buf[512];
+
+ DbgPrint("file0:++ %s\n",f);
+
+ fd = open(f,O_RDONLY);
+
+ ASSERT(fd != -1);
+
+ rc = read(fd,buf,512);
+
+ ASSERT(rc != -1);
+
+ rc = close(fd);
+
+ ASSERT(rc != -1);
+
+ DbgPrint("file0:--\n");
+}
+
+VOID
+file1(char *f)
+{
+ int rcb,wcb,ifd,ofd;
+ char buf[512], testbuf[128];
+ struct stat statbuf;
+ struct stat *ps;
+ int i;
+
+ DbgPrint("file1:++ %s\n",f);
+
+ ps = &statbuf;
+ // stat always fails with ENOENT for now - see comment in psx/fdapi.c
+
+ rcb = stat(f, ps);
+ if (rcb == -1)
+ DbgPrint("FAIL on stat: errno = %d\n", errno);
+ else {
+ DbgPrint("Mode = %lx, ino = %lx, dev = %ld, nlink = %ld, uid = %lx\n",
+ ps->st_mode, ps->st_ino, ps->st_dev, ps->st_nlink, ps->st_uid);
+ DbgPrint("gid = %lx, size = %ld, atime = %lx, mtime = %lx, ctime = %lx\n",
+ ps->st_gid, ps->st_size, ps->st_atime, ps->st_mtime, ps->st_ctime);
+ }
+
+ ifd = open(f,O_RDONLY);
+ ASSERT(ifd != -1);
+
+ ofd = open("out.dat",O_WRONLY | O_TRUNC);
+ ASSERT(ofd != -1);
+
+ do {
+ rcb = read(ifd,buf,512);
+ ASSERT(rcb != -1);
+ wcb = write(ofd,buf,rcb);
+ ASSERT(wcb != -1);
+ } while (rcb == 512);
+
+ rcb = close(ofd);
+ ASSERT(rcb != -1);
+
+ ofd = open("out.dat",O_RDWR);
+ ASSERT(ofd != -1);
+
+ for (i = 0; i < 128; i++) {
+ testbuf[i] = (char) i;
+ buf[i] = 0;
+ }
+
+ wcb = write(ofd,testbuf,128);
+ ASSERT(wcb != -1);
+
+ lseek(ofd, 0L, SEEK_SET);
+ rcb = read(ofd,buf,128);
+ ASSERT(rcb != -1);
+ if (rcb == -1)
+ DbgPrint("errno = %d\n", errno);
+
+ for (i = 0; i < 128; i++) {
+ if (buf[i] != testbuf[i]) {
+ DbgPrint("FAIL buffer contents check at %d\n", i);
+ for (i = 0; i < 128; i++) {
+ DbgPrint("%d ", buf[i]);
+ }
+ DbgPrint("\n");
+ break;
+ }
+ }
+
+ DbgPrint("Testing fstat on %s\n", f);
+ rcb = fstat(ifd, ps);
+ if (rcb == -1)
+ DbgPrint("FAIL on fstat: errno = %d\n", errno);
+ else
+ DbgPrint("Mode = %lx, ino = %lx, dev = %ld, nlink = %ld, uid = %lx\n",
+ ps->st_mode, ps->st_ino, ps->st_dev, ps->st_nlink, ps->st_uid);
+ DbgPrint("gid = %lx, size = %ld, atime = %lx, mtime = %lx, ctime = %lx\n",
+ ps->st_gid, ps->st_size, ps->st_atime, ps->st_mtime, ps->st_ctime);
+
+ rcb = close(ifd);
+ ASSERT(rcb != -1);
+ rcb = close(ofd);
+ ASSERT(rcb != -1);
+
+ DbgPrint("file1:--\n");
+}
+
+VOID
+file2(char *f1,char *f2)
+{
+ int fd;
+
+ DbgPrint("file2:++ %s %s\n",f1,f2);
+
+ fd = open(f1, O_RDONLY | O_CREAT | O_EXCL, 0);
+ ASSERT(fd == -1 && errno == EEXIST);
+ if (fd == -1 && errno != EEXIST)
+ DbgPrint("FAIL: errno = %d\n", errno);
+
+ DbgPrint("file2:--\n");
+}
+
+
+VOID
+file3(char *f)
+{
+ int rc, fd, fd2;
+ char buf[512];
+
+ DbgPrint("file3:++ %s - Testing dup\n",f);
+
+ fd = open(f,O_RDONLY);
+
+ ASSERT(fd != -1);
+
+ rc = read(fd,buf,512);
+
+ ASSERT(rc != -1);
+
+ fd2 = dup(fd);
+
+ ASSERT(fd2 != -1);
+
+ rc = close(fd);
+
+ ASSERT(rc != -1);
+
+ rc = read(fd2,buf,512);
+
+ ASSERT(rc != -1);
+
+ rc = close(fd2);
+
+ ASSERT(rc != -1);
+
+ DbgPrint("file3:--\n");
+}
+
+VOID
+file4(char *f1,char *f2)
+{
+ int rc, fd, fd2, fd3;
+ char buf[512];
+
+ DbgPrint("file4:++ %s %s - Testing dup2\n",f1, f2);
+
+ fd = open(f1,O_RDONLY);
+ fd2 = open(f2,O_RDONLY);
+ fd3 = open(f2,O_RDONLY);
+
+ ASSERT(fd != -1 && fd2 != -1 && fd3 != -1);
+
+ rc = read(fd,buf,512);
+
+ ASSERT(rc != -1);
+
+ rc = read(fd2,buf,512);
+
+ ASSERT(rc != -1);
+
+ fd2 = dup2(fd, fd2);
+
+ ASSERT(fd2 != -1);
+
+ rc = close(fd);
+
+ ASSERT(rc != -1);
+
+ rc = close(fd3);
+
+ ASSERT(rc != -1);
+
+ rc = read(fd2,buf,512);
+
+ ASSERT(rc != -1);
+
+ rc = close(fd2);
+
+ ASSERT(rc != -1);
+
+ DbgPrint("file4:--\n");
+}
diff --git a/private/posix/client/tst/tstfork.c b/private/posix/client/tst/tstfork.c
new file mode 100644
index 000000000..c3a316a19
--- /dev/null
+++ b/private/posix/client/tst/tstfork.c
@@ -0,0 +1,598 @@
+#include <nt.h>
+#include <ntrtl.h>
+
+#include "psxmsg.h"
+#include <signal.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <sys/times.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#include "tsttmp.h" // defines DbgPrint as printf
+
+extern int errno;
+
+VOID p0(VOID);
+VOID p1(VOID);
+VOID ex0(VOID);
+VOID a0(VOID);
+VOID s0(VOID);
+VOID call0(VOID);
+VOID f1(VOID);
+VOID f2(VOID);
+VOID p0foo(VOID);
+
+CONTEXT a0JumpBuff;
+
+int _CRTAPI1 main(int argc, char *argv[])
+{
+
+ pid_t self;
+ PCH p,t;
+ PTEB ThreadInfo;
+
+ ThreadInfo = NtCurrentTeb();
+
+ self = getpid();
+
+ DbgPrint("tstfork: My pid is %lx Argc = %lx\n",self,argc);
+ DbgPrint("tstfork: StackBase %lx\n",ThreadInfo->NtTib.StackBase);
+ DbgPrint("tstfork: StackLimit %lx\n",ThreadInfo->NtTib.StackLimit);
+ DbgPrint("tstfork: ClientId %lx.%lx\n",ThreadInfo->ClientId.UniqueProcess,ThreadInfo->ClientId.UniqueThread);
+
+ while(argc--){
+ p = *argv++;
+ t = p;
+ while(*t++);
+ DbgPrint("Argv --> %s\n",p);
+ }
+
+
+ p0();
+ p1();
+ ex0();
+ a0();
+ s0();
+ call0();
+ f1();
+ f2();
+
+ _exit(2);
+ return 1;
+
+}
+
+VOID
+p0foo(){}
+
+ULONG
+p0touch(
+ IN PUCHAR pch,
+ IN ULONG nb
+ )
+{
+ ULONG ct = 0;
+
+ DbgPrint("p0touch: pch %lx nb %lx\n",pch,nb);
+
+ while (nb--) {
+ ct += *pch++;
+ }
+ return ct;
+}
+
+VOID
+p0()
+{
+ int fildes[2];
+ int psxst;
+ UCHAR buffer[525];
+ UCHAR buf[525];
+ int i;
+
+ for(i=0;i<525;i++){
+ buf[i] = (char)(i&255);
+ }
+
+ DbgPrint("p0:++\n");
+
+ //
+ // Create a pipe
+ //
+
+ psxst = pipe(fildes);
+
+ if ( psxst ) {
+ DbgPrint("p0: pipe() failed st = %lx errno %lx\n",psxst,errno);
+ return;
+ }
+
+ DbgPrint("p0: fildes[0] = %lx filedes[1] = %lx\n",fildes[0],fildes[1]);
+
+ //
+ // Test write followed by read
+ //
+
+ psxst = write(fildes[1],"Hello World\n",13);
+ if ( psxst < 0 ) {
+ DbgPrint("p0: write() failed st = %lx errno %lx\n",psxst,errno);
+ return;
+ }
+
+ DbgPrint("p0: write() success transfered %lx bytes to fd %lx\n",psxst,fildes[1]);
+
+ psxst = read(fildes[0],buffer,32);
+ if ( psxst < 0 ) {
+ DbgPrint("p0: read() failed st = %lx errno %lx\n",psxst,errno);
+ return;
+ }
+
+ p0touch(buffer,psxst);
+ DbgPrint("p0: read() success transfered %lx bytes from fd %lx %s\n",psxst,fildes[1],buffer);
+ //
+ // End write followed by read
+ //
+
+
+ //
+ // assume parents read will happen before childs write.
+ // parents first read blocks testing write unblocks read
+ //
+
+ if ( !fork() ) {
+
+
+ DbgPrint("p0:child pid %lx\n",getpid());
+ DbgPrint("p0:child writing\n");
+
+ sleep(8);
+
+ psxst = write(fildes[1],"From Child\n\0",12);
+ if ( psxst < 0 ) {
+ DbgPrint("p0:child write() failed st = %lx errno %lx\n",psxst,errno);
+ return;
+ }
+ DbgPrint("p0:child write() success transfered %lx bytes to fd %lx\n",psxst,fildes[1]);
+
+ psxst = write(fildes[1],"Small Write\n\0",13);
+ if ( psxst < 0 ) {
+ DbgPrint("p0:child write() failed st = %lx errno %lx\n",psxst,errno);
+ return;
+ }
+ DbgPrint("p0:child write() success transfered %lx bytes to fd %lx\n",psxst,fildes[1]);
+
+ //
+ // this write should block and get unblocked when read of
+ // previous write completes
+ //
+ p0foo();
+ psxst = write(fildes[1],buf,514);
+ if ( psxst < 0 ) {
+ DbgPrint("p0:child write() failed st = %lx errno %lx\n",psxst,errno);
+ return;
+ }
+ DbgPrint("p0:child write() success transfered %lx bytes to fd %lx\n",psxst,fildes[1]);
+
+ _exit(1);
+ }
+
+ DbgPrint("p0:parent reading\n");
+ psxst = read(fildes[0],buffer,12);
+
+ if ( psxst < 0 ) {
+ DbgPrint("p0:parent read() failed st = %lx errno %lx\n",psxst,errno);
+ return;
+ }
+ p0touch(buffer,psxst);
+ DbgPrint("p0:parent read() success transfered %lx bytes from fd %lx %s\n",psxst,fildes[1],buffer);
+
+ DbgPrint("p0:parent sleeping\n");
+ sleep(12);
+ DbgPrint("p0:parent back from sleep\n");
+
+ DbgPrint("p0:parent reading\n");
+ psxst = read(fildes[0],buffer,13);
+ if ( psxst < 0 ) {
+ DbgPrint("p0:parent read() failed st = %lx errno %lx\n",psxst,errno);
+ return;
+ }
+ p0touch(buffer,psxst);
+ DbgPrint("p0:parent read() success transfered %lx bytes from fd %lx %s\n",psxst,fildes[1],buffer);
+
+ psxst = read(fildes[0],buffer,512);
+ if ( psxst < 0 ) {
+ DbgPrint("p0:parent read() failed st = %lx errno %lx\n",psxst,errno);
+ return;
+ }
+ DbgPrint("p0:parent read() success transfered %lx bytes from fd %lx\n",psxst,fildes[1]);
+
+ for(i=0;i<psxst;i++){
+ if ( buf[i] != buffer[i] ) {
+ DbgPrint("p0: Compare failed. i %lx master %lx vs. %lx\n",i,buf[i],buffer[i]);
+ }
+ }
+
+ psxst = read(fildes[0],buffer,512);
+ if ( psxst < 0 ) {
+ DbgPrint("p0:parent read() failed st = %lx errno %lx\n",psxst,errno);
+ return;
+ }
+ DbgPrint("p0:parent read() success transfered %lx bytes from fd %lx\n",psxst,fildes[1]);
+
+ for(i=0;i<psxst;i++){
+ if ( buf[i] != buffer[i] ) {
+ DbgPrint("p0: Compare failed. i %lx master %lx vs. %lx\n",i,buf[i],buffer[i]);
+ }
+ }
+ wait(NULL);
+
+ close(fildes[0]);
+ close(fildes[1]);
+
+ DbgPrint("p0:--\n");
+
+}
+
+VOID
+p1()
+{
+ int fildes[2];
+ int psxst;
+ UCHAR buffer[525];
+ UCHAR buf[525];
+ int i;
+
+ for(i=0;i<525;i++){
+ buf[i] = (char)(i&255);
+ }
+
+ DbgPrint("p1:++\n");
+
+ //
+ // Create a pipe
+ //
+
+ psxst = pipe(fildes);
+
+ if ( psxst ) {
+ DbgPrint("p1: pipe() failed st = %lx errno %lx\n",psxst,errno);
+ return;
+ }
+
+ DbgPrint("p1: fildes[0] = %lx filedes[1] = %lx\n",fildes[0],fildes[1]);
+
+ //
+ // Test write to readonly fd
+ //
+
+ psxst = write(fildes[0],"Hello World\n",13);
+ if ( psxst < 0 ) {
+ DbgPrint("p1: write test NT_SUCCESS %lx errno %lx\n",psxst,errno);
+ }
+
+ //
+ // Test read to writeonly fd
+ //
+
+ psxst = read(fildes[1],buf,13);
+ if ( psxst < 0 ) {
+ DbgPrint("p1: read test NT_SUCCESS %lx errno %lx\n",psxst,errno);
+ }
+
+
+ //
+ // Close Write Handle
+ //
+
+ close(fildes[1]);
+
+ //
+ // Test read w/ no writers
+ //
+
+ psxst = read(fildes[0],buffer,32);
+ if ( psxst == 0 ) {
+ DbgPrint("p1: read test NT_SUCCESS %lx\n",psxst);
+ }
+
+ close(fildes[0]);
+
+ DbgPrint("p1:--\n");
+
+}
+
+VOID
+ex0()
+{
+ PCH Arg[3], Env[4];
+ struct tms TimeBuffer;
+ clock_t TimesRtn;
+ int fildes[2];
+ int rc;
+
+ DbgPrint("ex0:++\n");
+
+ rc = pipe(fildes);
+ ASSERT(rc==0 && fildes[0] == 0 && fildes[1] == 1);
+
+ if ( !fork() ) {
+
+ if ( !fork() ) {
+
+ Arg[0]="tsthello.n10";
+ Arg[1]=(PCH)NULL;
+ Env[0]="NTUSER=MARKL";
+ Env[1]=(PCH)NULL;
+
+ //
+ // We should not have any accumulated child times
+ //
+
+ TimesRtn = times(&TimeBuffer);
+ ASSERT(TimesRtn != 0);
+ ASSERT(TimeBuffer.tms_cstime == 0 && TimeBuffer.tms_cutime == 0);
+
+ write(1,&TimeBuffer,sizeof(TimeBuffer));
+ execve("\\DosDevices\\D:\\PSX\\TSTHELLO.exe",Arg,Env);
+
+ DbgPrint("ex0:child returned from exec ? errno %lx\n",errno);
+
+ _exit(1);
+ }
+
+ wait(NULL);
+
+ //
+ // We should not have any accumulated child times
+ //
+
+ TimesRtn = times(&TimeBuffer);
+ ASSERT(TimesRtn != 0);
+ DbgPrint("tms_cstime %lx tms_cutime %lx\n",TimeBuffer.tms_cstime,TimeBuffer.tms_cutime);
+ ASSERT(TimeBuffer.tms_cstime != 0 || TimeBuffer.tms_cutime != 0);
+
+ _exit(2);
+ }
+
+ wait(NULL);
+
+ DbgPrint("ex0:parent wait satisfied \n");
+
+ close(fildes[0]);
+ close(fildes[1]);
+
+ DbgPrint("ex0:--\n");
+
+}
+
+VOID
+call0()
+{
+ VOID PdxNullPosixApi();
+
+#ifdef SIMULATOR
+ for(i=0;i<10;i++) {
+ begin = rnuminstr();
+ PdxNullPosixApi();
+ end = rnuminstr();
+
+ DbgPrint("Call Time 0x%lx 0x%lx 0x%lx dec %ld\n",begin, end, end - begin,end-begin);
+ }
+#endif // SIMULATOR
+
+}
+
+VOID
+f1()
+{
+ pid_t child,wchild;
+
+ DbgPrint("f1:+++\n");
+
+ child = fork();
+
+ if ( child == 0 ) {
+ DbgPrint("tstfork_child: I am the child %lx\n",getpid());
+ _exit(1);
+ }
+
+ DbgPrint("tstfork_parent: My child is %lx\n",child);
+
+ wchild = wait(NULL);
+
+ DbgPrint("tstfork_parent: Wait on child %lx satisfied %lx errno %lx\n",
+ child,
+ wchild,
+ errno
+ );
+ DbgPrint("f1:---\n");
+}
+
+void
+_CRTAPI1
+f2catcher(
+ IN int sig
+ )
+{
+ DbgPrint("f2catcher, signal == %lx\n",sig);
+}
+
+VOID
+f2()
+{
+ struct sigaction act, oact;
+ pid_t child,wchild,parent;
+ int psxst;
+
+
+ //
+ // Send signal to parent which sould be in wait. Parent should
+ // get EINTR from wait. If it retries, then wait will succeed.
+ //
+
+ DbgPrint("f2:+++\n");
+
+ act.sa_handler = f2catcher;
+ sigfillset(&act.sa_mask);
+ act.sa_flags = 0;
+
+ if (sigaction(SIGUSR1, &act ,&oact) ) {
+ DbgPrint("f2: fail sigaction errno %lx\n",errno);
+ _exit(-1);
+ }
+
+ child = fork();
+ if( !child ) {
+
+ DbgPrint("tstfork_child: I am the child %lx parent %lx\n",
+ getpid(),
+ (parent = getppid())
+ );
+
+ DbgPrint("tstfork_child: Killing SIGUSR1 parent %lx\n",parent);
+
+ psxst = kill(parent,SIGUSR1);
+
+ if ( psxst < 0 ) {
+ DbgPrint("tstfork_child: kill failed %lx errno %lx\n",psxst,errno);
+ _exit(errno);
+ }
+
+ _exit(1);
+ }
+
+ DbgPrint("tstfork_parent: My child is %lx\n",child);
+
+ wchild = wait(NULL);
+
+ DbgPrint("tstfork_parent: Wait on child %lx satisfied %lx errno %lx\n",
+ child,
+ wchild,
+ errno
+ );
+
+ if ( wchild < 0 && errno == EINTR ) {
+ DbgPrint("tstfork_parent: wait interrupted by SIGUSR1. Rewait\n");
+
+ wchild = wait(NULL);
+
+ DbgPrint("tstfork_parent: Wait on child %lx satisfied %lx errno %lx\n",
+ child,
+ wchild,
+ errno
+ );
+ }
+
+ DbgPrint("f2:---\n");
+}
+
+void
+_CRTAPI1
+a0catcher(
+ IN int sig
+ )
+{
+ DbgPrint("a0catcher, signal == %lx long jumping...\n",sig);
+ NtContinue(&a0JumpBuff,FALSE);
+}
+
+VOID
+a0()
+{
+ struct sigaction act, oact;
+ unsigned int previous;
+ NTSTATUS st;
+ sigset_t eset;
+
+ //
+ // Call alarm and then sit in a loop
+ //
+
+ DbgPrint("a0:+++\n");
+
+ act.sa_handler = a0catcher;
+ sigfillset(&act.sa_mask);
+ act.sa_flags = 0;
+
+ if (sigaction(SIGALRM, &act ,&oact) ) {
+ DbgPrint("a0: fail sigaction errno %lx\n",errno);
+ _exit(-1);
+ }
+
+ a0JumpBuff.ContextFlags = CONTEXT_FULL;
+
+ st = NtGetContextThread(NtCurrentThread(),&a0JumpBuff);
+
+#ifdef MIPS
+ a0JumpBuff.IntV0 = 0x12345678;
+#endif
+
+#ifdef i386
+ a0JumpBuff.Eax = 0x12345678;
+#endif
+
+ if (st == STATUS_SUCCESS) {
+ previous = alarm(2);
+ for(;;);
+ }
+
+ sigemptyset(&eset);
+ sigprocmask(SIG_SETMASK,&eset,NULL);
+
+ DbgPrint("a0: Longjumpp st = %lx\n",st);
+
+ //
+ // start a 10 second alarm and then cancel it
+ //
+
+ a0JumpBuff.ContextFlags = CONTEXT_FULL;
+
+ st = NtGetContextThread(NtCurrentThread(),&a0JumpBuff);
+
+#ifdef MIPS
+ a0JumpBuff.IntV0 = 0x12345678;
+#endif
+
+#ifdef i386
+ a0JumpBuff.Eax = 0x12345678;
+#endif
+
+ if (st == STATUS_SUCCESS) {
+ previous = alarm(10);
+ sleep(1);
+ previous = alarm(0);
+ DbgPrint("Previous = %lx\n",previous);
+ }
+
+ sigemptyset(&eset);
+ sigprocmask(SIG_SETMASK,&eset,NULL);
+
+ DbgPrint("a0:---\n");
+}
+
+VOID
+s0()
+{
+
+ LARGE_INTEGER BeginTime,EndTime;
+ unsigned int psxst;
+
+ DbgPrint("s0:+++\n");
+
+ NtQuerySystemTime(&BeginTime);
+
+ DbgPrint("s0: sleeping\n");
+
+ psxst = sleep(1);
+
+ NtQuerySystemTime(&EndTime);
+
+ DbgPrint("s0: sleep(1) returned %lx\n",psxst);
+ DbgPrint("s0: BeginTime %lx %lx\n",BeginTime.HighPart,BeginTime.LowPart);
+ DbgPrint("s0: EndTime %lx %lx\n",EndTime.HighPart,EndTime.LowPart);
+
+ DbgPrint("s0:---\n");
+
+}
diff --git a/private/posix/client/tst/tstheap.c b/private/posix/client/tst/tstheap.c
new file mode 100644
index 000000000..5752dbd64
--- /dev/null
+++ b/private/posix/client/tst/tstheap.c
@@ -0,0 +1,111 @@
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <malloc.h>
+
+//
+// 'tstheap.c'
+// Tests out malloc, calloc, realloc, and mfree.
+//
+// 05/22/92 DarekM Created
+//
+
+#define max(a,b) ((a > b) ? a : b )
+
+
+int
+main(int argc, char *argv[])
+{
+ int l; // loop counter
+ int t; // total memory allocated
+ int c; // count of blocks
+ int i; // current block index
+ void *p; // address of block
+ int numblocks;
+ int delta;
+ void **rgp; // array of memory pointers
+ int fDEBUG = 0;
+
+ if (argc < 3)
+ {
+ printf("Usage: tstheap <numblocks> <delta> [DUMP]\n\n");
+ return;
+ }
+ else if (argc > 3)
+ fDEBUG = 1;
+
+ numblocks = max(1, atoi(argv[1]));
+ delta = max(1, atoi(argv[2]));
+ rgp = malloc(numblocks * sizeof(void *));
+
+ printf("TstHeap: numblocks = %d, delta = %d\n\n", numblocks, delta);
+
+ for (l = 0; ; l++)
+ {
+ t = c = 0;
+
+ printf("PASS #%d\n", l);
+
+ for (i = 0; i < numblocks; i++)
+ {
+ int cb;
+
+ if (i & 1)
+ p = malloc(cb = i + l*delta + 1);
+ else
+ p = calloc(cb = i + l*delta + (rand() & 255) + 1, 1);
+
+ if (p == NULL)
+ {
+ printf("p == NULL\n");
+ break;
+ }
+
+ if (((int)p < 0x1000) || ((int)p < 0))
+ {
+ printf("WIERD P == %d\n", p);
+ break;
+ }
+
+ rgp[i] = p;
+ t += cb;
+
+ if (fDEBUG)
+ printf(" %d,%02d: Alloced $%08X\n", l, i, p);
+ }
+
+ if ((c = i) == 0)
+ break;
+
+ printf(" Blocks alloced: %d Bytes: %d\n", c, t);
+
+ for (i = 0; i < c; i++)
+ {
+ rgp[i] = p = realloc(rgp[i], 1);
+
+ if (fDEBUG)
+ printf(" %d,%02d: Realloced $%08X\n", l, i, p);
+ }
+
+ printf(" Blocks realloced: %d\n", i);
+
+ for (i = 0; i < c; i++)
+ {
+ free(rgp[i]);
+
+ if (fDEBUG)
+ printf(" %d,%02d: Freed $%08X\n", l, i, rgp[i]);
+ }
+
+ printf(" Blocks freed: %d\n\n", i);
+ }
+
+ printf("\n\n");
+
+ free(rgp);
+ return 1;
+}
+
+
diff --git a/private/posix/client/tst/tsthello.c b/private/posix/client/tst/tsthello.c
new file mode 100644
index 000000000..a34d105f8
--- /dev/null
+++ b/private/posix/client/tst/tsthello.c
@@ -0,0 +1,38 @@
+#include <nt.h>
+#include <ntrtl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/times.h>
+
+#include "tsttmp.h" // defines DbgPrint as printf
+
+int
+main(int argc, char *argv[])
+
+{
+ PCH p,t;
+ CHAR Buf[128],c,*b;
+ struct tms *TimeBuffer;
+ int psxst;
+
+ while(argc--){
+ p = *argv++;
+ t = p;
+ while(*t++);
+ DbgPrint("Argv --> %s\n",p);
+ }
+
+ b = getcwd(Buf,128);
+ ASSERT(b);
+
+ psxst = read(0,Buf,128);
+ if ( psxst > 0 ) {
+ if ( psxst == sizeof(*TimeBuffer) ) {
+ DbgPrint("time buffer received\n");
+ } else {
+ c = Buf[0];
+ DbgPrint("hello_main: Pipe Read %s\n",Buf,c);
+ }
+ }
+ return 1;
+}
diff --git a/private/posix/client/tst/tsthw.c b/private/posix/client/tst/tsthw.c
new file mode 100644
index 000000000..3327c3024
--- /dev/null
+++ b/private/posix/client/tst/tsthw.c
@@ -0,0 +1,20 @@
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include "tsttmp.h" // defines DbgPrint as printf
+
+//
+// 'tsthw'
+// First step 'hellow world' test
+//
+
+int
+main(int argc, char *argv[])
+{
+ DbgPrint("Hello World\n");
+ return 1;
+}
+
diff --git a/private/posix/client/tst/tstjc.c b/private/posix/client/tst/tstjc.c
new file mode 100644
index 000000000..68f559a0e
--- /dev/null
+++ b/private/posix/client/tst/tstjc.c
@@ -0,0 +1,367 @@
+#include <nt.h>
+#include <ntrtl.h>
+
+#include <signal.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "tsttmp.h" // defines DbgPrint as printf
+
+extern int errno;
+VOID jc0(VOID);
+VOID jc1(VOID);
+VOID jc2(VOID);
+VOID jc3(VOID);
+
+int
+main(int argc, char *argv[])
+{
+
+ pid_t self;
+ PCH p,t;
+ PTEB ThreadInfo;
+
+ ThreadInfo = NtCurrentTeb();
+
+ self = getpid();
+
+ DbgPrint("jc: My pid is %lx Argc = %lx\n",self,argc);
+ DbgPrint("jc: StackBase %lx\n",ThreadInfo->NtTib.StackBase);
+ DbgPrint("jc: StackLimit %lx\n",ThreadInfo->NtTib.StackLimit);
+ DbgPrint("jc: ClientId %lx.%lx\n",ThreadInfo->ClientId.UniqueProcess,ThreadInfo->ClientId.UniqueThread);
+
+ while(argc--){
+ p = *argv++;
+ t = p;
+ while(*t++);
+ DbgPrint("Argv --> %s\n",p);
+ }
+
+ jc0();
+ jc1();
+ jc2();
+ jc3();
+
+ return 1;
+}
+
+
+VOID
+jc0()
+{
+ pid_t child;
+ int rc,stat_loc;
+ struct sigaction act;
+
+
+ DbgPrint("jc0:++\n");
+
+ //
+ // Ignore SIGCHLD signals
+ //
+
+ act.sa_flags = SA_NOCLDSTOP;
+ act.sa_handler = SIG_IGN;
+ rc = sigaction(SIGCHLD, &act, NULL);
+ ASSERT( rc == 0 );
+
+ child = fork();
+
+ if ( !child) {
+
+ for(;;);
+
+ ASSERT(0 != 0);
+ }
+
+ rc = kill(child,SIGSTOP);
+ ASSERT(rc==0);
+
+ //
+ // Make sure that wait is satisfied by stopped child
+ //
+
+ rc = waitpid(child,&stat_loc,WUNTRACED);
+ ASSERT(rc == child && WIFSTOPPED(stat_loc) && WSTOPSIG(stat_loc) == SIGSTOP);
+
+ //
+ // Also make sure that it's status may only be picked up once
+ //
+
+ rc = waitpid(child,NULL,WUNTRACED | WNOHANG);
+ ASSERT(rc == 0);
+
+ //
+ // SEGV the process. Since it is stopped, this should have no effect
+ //
+
+ rc = kill(child,SIGSEGV);
+ ASSERT(rc==0);
+
+ rc = waitpid(child,NULL,WUNTRACED | WNOHANG);
+ ASSERT(rc == 0);
+
+ //
+ // Kill the process w/ SIGKILL. This should doit
+ //
+
+ rc = kill(child,SIGKILL);
+ ASSERT(rc==0);
+
+ rc = waitpid(child,&stat_loc,0);
+ ASSERT(rc == child && WIFSIGNALED(stat_loc) && WTERMSIG(stat_loc) == SIGKILL);
+
+ DbgPrint("jc0:--\n");
+}
+
+int thechild;
+
+void
+jc1_sigchld_handler(
+ IN int sig
+ )
+{
+ int rc, stat_loc;
+ struct sigaction act;
+
+ ASSERT(sig == SIGCHLD);
+ rc = waitpid(thechild,&stat_loc,0);
+ ASSERT(rc == thechild && WIFEXITED(stat_loc) && WEXITSTATUS(stat_loc) == 100);
+
+ act.sa_flags = 0;
+ sigfillset(&act.sa_mask);
+ act.sa_handler = SIG_IGN;
+ rc = sigaction(SIGCHLD, &act, NULL);
+ ASSERT( rc == 0 );
+}
+
+void
+jc1_sigcont_handler(
+ IN int sig
+ )
+{
+ ASSERT(sig == SIGCONT);
+ _exit(100);
+}
+
+VOID
+jc1()
+{
+ int rc,stat_loc;
+ struct sigaction act;
+ sigset_t set,oset;
+
+ DbgPrint("jc1:++\n");
+
+ //
+ // Catch SIGCHLD signals
+ //
+
+ act.sa_flags = 0;
+ sigfillset(&act.sa_mask);
+ act.sa_handler = jc1_sigchld_handler;
+ rc = sigaction(SIGCHLD, &act, NULL);
+ ASSERT( rc == 0 );
+
+ //
+ // Catch SIGCONT. This is really set up for Child
+ //
+
+ act.sa_flags = 0;
+ sigfillset(&act.sa_mask);
+ act.sa_handler = jc1_sigcont_handler;
+ rc = sigaction(SIGCONT, &act, NULL);
+ ASSERT( rc == 0 );
+
+ thechild = fork();
+
+ if ( !thechild) {
+
+ for(;;);
+
+ _exit(99);
+
+ ASSERT(0 != 0);
+ }
+
+ //
+ // Block SIGCHLD
+ //
+
+ sigfillset(&set);
+ rc = sigprocmask(SIG_SETMASK,&set,&oset);
+ ASSERT(rc==0);
+
+ rc = kill(thechild,SIGSTOP);
+ ASSERT(rc==0);
+
+ //
+ // Make sure that wait is satisfied by stopped child
+ //
+
+ rc = waitpid(thechild,&stat_loc,WUNTRACED);
+ ASSERT(rc == thechild && WIFSTOPPED(stat_loc) && WSTOPSIG(stat_loc) == SIGSTOP);
+
+ //
+ // SIGCONT the process.
+ //
+
+ rc = kill(thechild,SIGCONT);
+ ASSERT(rc==0);
+
+ //
+ // Unblock SIGCHLD
+ //
+
+ rc = sigprocmask(SIG_SETMASK,&oset,NULL);
+ ASSERT(rc==0);
+
+ rc = waitpid(thechild,&stat_loc,WUNTRACED);
+ ASSERT( rc == -1 && errno == ECHILD);
+
+ act.sa_flags = 0;
+ sigfillset(&act.sa_mask);
+ act.sa_handler = SIG_DFL;
+ rc = sigaction(SIGCONT, &act, NULL);
+
+ DbgPrint("jc1:--\n");
+}
+
+VOID
+jc2()
+{
+ pid_t child, OrigGroup;
+ int rc,stat_loc;
+
+ DbgPrint("jc2:++\n");
+
+ OrigGroup = getpgrp();
+
+ //
+ // Should be process group leader
+ //
+
+ ASSERT(getpid() == OrigGroup);
+
+ //
+ // Fork. Then have child establish its own group.
+ // Child and parent are in then in different groups,
+ // but in the same session.
+ //
+
+ if ( !fork() ) {
+
+ rc = setpgid(0,0);
+ ASSERT(rc==0 && getpgrp() == getpid());
+
+ child = fork();
+
+ if ( !child ) {
+
+ rc = kill(getpid(),SIGSTOP);
+ ASSERT(rc==0);
+ }
+
+ rc = waitpid(child,&stat_loc,WUNTRACED);
+ ASSERT(rc == child && WIFSTOPPED(stat_loc) && WSTOPSIG(stat_loc) == SIGSTOP);
+
+ //
+ // Conditions are set. If this process exits, then its group
+ // will zombie. Stopped process should continue w/ SIGCONT/SIGHUP
+ //
+
+ _exit(123);
+
+ }
+
+ rc = wait(&stat_loc);
+ ASSERT(WIFEXITED(stat_loc) && WEXITSTATUS(stat_loc) == 123);
+ sleep(10);
+
+ DbgPrint("jc2:--\n");
+}
+
+VOID
+jc3()
+{
+ pid_t child, OrigGroup;
+ int rc,stat_loc;
+
+ DbgPrint("jc3:++\n");
+
+ OrigGroup = getpgrp();
+
+ //
+ // Should be process group leader
+ //
+
+ ASSERT(getpid() == OrigGroup);
+
+ //
+ // Fork. Then have child establish its own group.
+ // Child and parent are in then in different groups,
+ // but in the same session.
+ //
+
+ if ( !fork() ) {
+
+ rc = setpgid(0,0);
+ ASSERT(rc==0 && getpgrp() == getpid());
+
+ child = fork();
+
+ if ( !child ) {
+ struct sigaction act;
+
+ //
+ // Child should ignore SIGHUP
+ //
+
+ act.sa_flags = SA_NOCLDSTOP;
+ act.sa_handler = SIG_IGN;
+ rc = sigaction(SIGHUP, &act, NULL);
+ ASSERT( rc == 0 );
+
+ rc = kill(getpid(),SIGSTOP);
+ ASSERT(rc==0);
+
+ //
+ // parents exit SIGCONTs child. At this point child
+ // is part of an orphaned process group. The process
+ // should not stop in response to SIGTSTP, SIGTTIN,
+ // or SIGTTOU
+ //
+
+ rc = kill(getpid(),SIGTSTP);
+ ASSERT(rc==0);
+
+ rc = kill(getpid(),SIGTTIN);
+ ASSERT(rc==0);
+
+ rc = kill(getpid(),SIGTTOU);
+ ASSERT(rc==0);
+
+ _exit(8);
+
+ }
+
+ rc = waitpid(child,&stat_loc,WUNTRACED);
+ ASSERT(rc == child && WIFSTOPPED(stat_loc) && WSTOPSIG(stat_loc) == SIGSTOP);
+
+ //
+ // Conditions are set. If this process exits, then its group
+ // will zombie. Stopped process should continue w/ SIGCONT/SIGHUP
+ //
+
+ _exit(123);
+
+ }
+
+ rc = wait(&stat_loc);
+ ASSERT(WIFEXITED(stat_loc) && WEXITSTATUS(stat_loc) == 123);
+ sleep(10);
+
+ DbgPrint("jc3:--\n");
+}
diff --git a/private/posix/client/tst/tstloop.c b/private/posix/client/tst/tstloop.c
new file mode 100644
index 000000000..7c26b6ca2
--- /dev/null
+++ b/private/posix/client/tst/tstloop.c
@@ -0,0 +1,83 @@
+#include <nt.h>
+#include <ntrtl.h>
+
+#include "psxmsg.h"
+#include <unistd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "tsttmp.h" // defines DbgPrint as printf
+
+extern int errno;
+
+void
+_CRTAPI1
+catcher(
+ IN int sig
+ );
+
+int caught_sig;
+
+int
+_CRTAPI1
+main(int argc, char *argv[])
+{
+ LONG i;
+ int e;
+ struct sigaction act, oact;
+ sigset_t eset,fset;
+ pid_t pid;
+ LARGE_INTEGER DelayTime;
+
+ pid = getpid();
+
+ DbgPrint("Looper Posix Process... Pid = %lx\n\n",pid);
+
+ DbgPrint("Looper Delay\n");
+ DelayTime.HighPart = -1;
+ DelayTime.LowPart = -100000;
+ NtDelayExecution(FALSE,&DelayTime);
+ DbgPrint("Looper Delay Done\n");
+
+ _exit(8);
+
+ sigemptyset(&eset);
+ sigfillset(&fset);
+ sigdelset(&fset,SIGHUP);
+
+ act.sa_handler = catcher;
+ sigfillset(&act.sa_mask);
+ act.sa_flags = 0;
+
+ if (sigaction(SIGUSR1, &act ,&oact) ) {
+ DbgPrint("main: fail sigaction errno %lx\n",errno);
+ _exit(-1);
+ }
+
+ for(i=1;i<0x100000;i++){
+ if ( (i & 0xfff) == 0 ) DbgPrint("Looper: i == %lx\n",i);
+ if ( (i & 0x7fff) == 0) {
+ DbgPrint("Looper: calling sigprocmask i == %lx\n",i);
+ sigprocmask(SIG_SETMASK,&fset,NULL);
+ DbgPrint("Looper: calling sigsuspend i == %lx\n",i);
+ e = sigsuspend(&eset);
+ DbgPrint("Looper: returned from sigsuspend %lx errno %lx i %lx\n",e,errno,i);
+ }
+ }
+
+ DbgPrint("Looper: Exiting...\n");
+
+ return 1;
+}
+
+
+void
+_CRTAPI1
+catcher(
+ IN int sig
+ )
+{
+ DbgPrint("Looper: In Catcher, signal == %lx\n",sig);
+ caught_sig = 1;
+}
diff --git a/private/posix/client/tst/tstmd.c b/private/posix/client/tst/tstmd.c
new file mode 100644
index 000000000..727880c50
--- /dev/null
+++ b/private/posix/client/tst/tstmd.c
@@ -0,0 +1,54 @@
+#include <nt.h>
+#include <ntrtl.h>
+
+#include <signal.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#include "tsttmp.h" // defines DbgPrint as printf
+
+extern int errno;
+VOID mkdir0(char *);
+
+
+int
+_CRTAPI1
+main(int argc, char *argv[])
+{
+
+ if (argc != 2) {
+ DbgPrint("Usage: 'tstmkdir dirname'\n");
+ return 1;
+ }
+
+ mkdir0(argv[1]);
+
+ return 1;
+}
+
+
+VOID
+mkdir0(char *f)
+{
+ int rc;
+
+ DbgPrint("mkdir0:++ %s\n",f);
+
+ DbgPrint("creating directory %s\n", f);
+ rc = mkdir(f,0);
+ ASSERT(rc != -1);
+
+ DbgPrint("attempting to recreate existing directory %s\n", f);
+ rc = mkdir(f,0);
+ ASSERT(rc == -1 && errno == EEXIST);
+
+ DbgPrint("removing directory %s\n", f);
+ rc = rmdir(f);
+ ASSERT(rc != -1);
+
+ DbgPrint("mkdir0:--\n");
+}
diff --git a/private/posix/client/tst/tstmisc.c b/private/posix/client/tst/tstmisc.c
new file mode 100644
index 000000000..2b70950c9
--- /dev/null
+++ b/private/posix/client/tst/tstmisc.c
@@ -0,0 +1,242 @@
+
+#include <nt.h>
+#include <ntrtl.h>
+
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/wait.h>
+#include <sys/utsname.h>
+#include <sys/times.h>
+
+#include "tsttmp.h" // defines DbgPrint as printf
+
+extern int errno;
+VOID sysconf0(void);
+VOID pathconf0(void);
+VOID fpathconf0(void);
+VOID uname0(void);
+VOID time0(void);
+
+//
+// 'tstmisc'
+// tests sysconf(), pathconf(), fpathconf(), uname(), time(), times()
+// The file /psx/test/conffile should exist
+//
+
+int
+_CRTAPI1
+main(int argc, char *argv[])
+{
+
+ if (argc != 1) {
+ DbgPrint("Usage: '%s'\n", argv[0]);
+ return 1;
+ }
+ sysconf0();
+ pathconf0();
+ fpathconf0();
+ uname0();
+ time0();
+
+ return 1;
+}
+
+
+VOID
+sysconf0(void)
+{
+ BOOLEAN fail = FALSE;
+
+ DbgPrint("sysconf0:++\n");
+ if (sysconf(_SC_ARG_MAX) != ARG_MAX) {
+ DbgPrint("sysconf FAIL on ARG_MAX\n");
+ fail = TRUE;
+ }
+ if (sysconf(_SC_CHILD_MAX) != CHILD_MAX) {
+ DbgPrint("sysconf FAIL on CHILD_MAX\n");
+ fail = TRUE;
+ }
+ if (sysconf(_SC_CLK_TCK) != CLK_TCK) {
+ DbgPrint("sysconf FAIL on CLK_TCK\n");
+ fail = TRUE;
+ }
+ if (sysconf(_SC_NGROUPS_MAX) != NGROUPS_MAX) {
+ DbgPrint("sysconf FAIL on NGROUPS_MAX\n");
+ fail = TRUE;
+ }
+ if (sysconf(_SC_OPEN_MAX) != OPEN_MAX) {
+ DbgPrint("sysconf FAIL on OPEN_MAX\n");
+ fail = TRUE;
+ }
+ if (sysconf(_SC_JOB_CONTROL) == 0L)
+ DbgPrint("sysconf JOB_CONTROL OFF\n");
+ else
+ DbgPrint("sysconf JOB_CONTROL ON\n");
+ if (sysconf(_SC_SAVED_IDS) == 0L)
+ DbgPrint("sysconf SAVED_IDS OFF\n");
+ else
+ DbgPrint("sysconf SAVED_IDS ON\n");
+ DbgPrint("sysconf VERSION = %d\n", sysconf(_SC_VERSION));
+
+ if (!fail)
+ DbgPrint("sysconf PASSED\n");
+ DbgPrint("sysconf0:--\n");
+}
+
+VOID pathconf0(void)
+{
+ BOOLEAN fail = FALSE;
+
+ DbgPrint("pathconf0:++\n");
+ if (pathconf("/psx/test/conffile", _PC_LINK_MAX) != LINK_MAX) {
+ DbgPrint("pathconf FAIL on LINK_MAX\n");
+ fail = TRUE;
+ }
+ if (pathconf("/psx/test/conffile", _PC_MAX_CANON) != MAX_CANON) {
+ DbgPrint("pathconf FAIL on MAX_CANON\n");
+ fail = TRUE;
+ }
+ if (pathconf("/psx/test/conffile", _PC_MAX_INPUT) != MAX_INPUT) {
+ DbgPrint("pathconf FAIL on MAX_INPUT\n");
+ fail = TRUE;
+ }
+ if (pathconf("/psx/test/conffile", _PC_NAME_MAX) != NAME_MAX) {
+ DbgPrint("pathconf FAIL on NAME_MAX\n");
+ fail = TRUE;
+ }
+ if (pathconf("/psx/test/conffile", _PC_PATH_MAX) != PATH_MAX) {
+ DbgPrint("pathconf FAIL on PATH_MAX\n");
+ fail = TRUE;
+ }
+ if (pathconf("/psx/test/conffile", _PC_PIPE_BUF) != PIPE_BUF) {
+ DbgPrint("pathconf FAIL on PIPE_BUF\n");
+ fail = TRUE;
+ }
+ if (pathconf("/psx/test/conffile", _PC_CHOWN_RESTRICTED) == 0L)
+ DbgPrint("pathconf CHOWN_RESTRICTED OFF\n");
+ else
+ DbgPrint("pathconf CHOWN_RESTRICTED ON\n");
+ if (pathconf("/psx/test/conffile", _PC_NO_TRUNC) == 0L)
+ DbgPrint("pathconf NO_TRUNC OFF\n");
+ else
+ DbgPrint("pathconf NO_TRUNC ON\n");
+ if (pathconf("/psx/test/conffile", _PC_VDISABLE) == 0L)
+ DbgPrint("pathconf VDISABLE OFF\n");
+ else
+ DbgPrint("pathconf VDISABLE ON\n");
+
+ if (!fail)
+ DbgPrint("pathconf PASSED\n");
+ DbgPrint("pathconf0:--\n");
+}
+
+VOID fpathconf0(void)
+{
+ BOOLEAN fail = FALSE;
+ int fd;
+
+ DbgPrint("fpathconf0:++\n");
+
+ if ( (fd = open("/psx/test/conffile", O_RDONLY)) == -1) {
+ DbgPrint("Cannot open /psx/test/conffile\n");
+ return;
+ }
+
+ if (fpathconf(fd, _PC_LINK_MAX) != LINK_MAX) {
+ DbgPrint("fpathconf FAIL on LINK_MAX\n");
+ fail = TRUE;
+ }
+ if (fpathconf(fd, _PC_MAX_CANON) != MAX_CANON) {
+ DbgPrint("fpathconf FAIL on MAX_CANON\n");
+ fail = TRUE;
+ }
+ if (fpathconf(fd, _PC_MAX_INPUT) != MAX_INPUT) {
+ DbgPrint("fpathconf FAIL on MAX_INPUT\n");
+ fail = TRUE;
+ }
+ if (fpathconf(fd, _PC_NAME_MAX) != NAME_MAX) {
+ DbgPrint("fpathconf FAIL on NAME_MAX\n");
+ fail = TRUE;
+ }
+ if (fpathconf(fd, _PC_PATH_MAX) != PATH_MAX) {
+ DbgPrint("fpathconf FAIL on PATH_MAX\n");
+ fail = TRUE;
+ }
+ if (fpathconf(fd, _PC_PIPE_BUF) != PIPE_BUF) {
+ DbgPrint("fpathconf FAIL on PIPE_BUF\n");
+ fail = TRUE;
+ }
+ if (fpathconf(fd, _PC_CHOWN_RESTRICTED) == 0L)
+ DbgPrint("fpathconf CHOWN_RESTRICTED OFF\n");
+ else
+ DbgPrint("fpathconf CHOWN_RESTRICTED ON\n");
+ if (fpathconf(fd, _PC_NO_TRUNC) == 0L)
+ DbgPrint("fpathconf NO_TRUNC OFF\n");
+ else
+ DbgPrint("fpathconf NO_TRUNC ON\n");
+ if (fpathconf(fd, _PC_VDISABLE) == 0L)
+ DbgPrint("fpathconf VDISABLE OFF\n");
+ else
+ DbgPrint("fpathconf VDISABLE ON\n");
+
+ if (!fail)
+ DbgPrint("fpathconf PASSED\n");
+ DbgPrint("fpathconf0:--\n");
+}
+
+VOID uname0(void)
+{
+ struct utsname name;
+ int rc;
+
+ DbgPrint("uname0:++\n");
+
+ rc = uname((struct utsname *) &name);
+ if (rc == -1) {
+ DbgPrint("FAIL call to uname, errno = %d.\n", errno);
+ }
+ else {
+ DbgPrint("sysname = %s\nnodename = %s(should be null)\nrelease = %s\nversion = %s\nmachine = %s\n",
+ name.sysname, name.nodename, name.release, name.version,
+ name.machine);
+ }
+
+ DbgPrint("uname0:--\n");
+}
+
+// This should translate to yy mm dd format
+
+VOID time0(void)
+{
+ time_t tloc;
+ time_t rc;
+
+ DbgPrint("time0:++\n");
+
+ rc = time((time_t *) &tloc);
+
+ ASSERT(rc == tloc);
+ DbgPrint("Seconds since the Epoch = %ld\n", tloc);
+
+ DbgPrint("time0:--\n");
+
+}
+
+VOID time1(void)
+{
+ struct tms tbuf;
+ clock_t rc;
+
+ DbgPrint("time1:++\n");
+
+ rc = times((struct tms *) &tbuf);
+
+ DbgPrint("stime = %ld, utime = %ld, cstime = %ld, cutime = %ld rc = %ld\n",
+ tbuf.tms_stime, tbuf.tms_utime,tbuf.tms_cstime,tbuf.tms_cutime,rc);
+
+ DbgPrint("time1:--\n");
+}
diff --git a/private/posix/client/tst/tstncall.c b/private/posix/client/tst/tstncall.c
new file mode 100644
index 000000000..1444cef36
--- /dev/null
+++ b/private/posix/client/tst/tstncall.c
@@ -0,0 +1,55 @@
+#include <nt.h>
+#include <ntrtl.h>
+
+#include "psxmsg.h"
+#include <signal.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "tsttmp.h" // defines DbgPrint as printf
+
+#define MEM_CTL 0xb0000000
+
+int main(int argc, char *argv[])
+{
+
+ call0();
+ _exit(0);
+ return 1;
+
+}
+
+call0()
+{
+ LONG x,i,j;
+ ULONG begin,end;
+ ULONG ibegin,iend;
+ VOID PdxNullPosixApi();
+ volatile PULONG MemCtl;
+
+ MemCtl = (PULONG) MEM_CTL;
+
+#ifdef SIMULATOR
+ for(i=0;i<10;i++) {
+ begin = rnuminstr();
+ ibegin = DbgQueryIoCounter();
+ PdxNullPosixApi();
+ iend = DbgQueryIoCounter();
+ end = rnuminstr();
+
+ DbgPrint("Call Time 0x%lx dec %ld IO %ld\n", end - begin,end-begin, iend-ibegin);
+ }
+#else
+ for(j=0;j<5;j++) {
+ DbgPrint("Starting 10000 Calls...");
+ x = *MemCtl;
+ for(i=0;i<10000;i++) {
+ PdxNullPosixApi();
+ }
+ x = *MemCtl;
+ DbgPrint("Complete\n");
+ }
+#endif // SIMULATOR
+}
diff --git a/private/posix/client/tst/tstnpipe.c b/private/posix/client/tst/tstnpipe.c
new file mode 100644
index 000000000..711d4ffeb
--- /dev/null
+++ b/private/posix/client/tst/tstnpipe.c
@@ -0,0 +1,420 @@
+#include <nt.h>
+#include <ntrtl.h>
+
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include "tsttmp.h" // defines DbgPrint as printf
+
+extern int errno;
+VOID npipe0(char *);
+VOID npipe1(char *);
+VOID npipe2(char *);
+VOID npipe3(char *);
+VOID npipe4(char *);
+VOID npipe5(VOID);
+
+//
+// 'tstnpipe named.pip'.
+//
+// The directory /psx/test is used as the base directory. The zero-length
+// file named 'named.pip must exist in that directory.
+//
+
+char
+nulltouch(char *f)
+{
+ return 'a';
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ if (argc != 2) {
+ DbgPrint("Usage: 'tstnpipe named.pip'\n");
+ return 1;
+ }
+ ASSERT(strcmp(argv[1],"named.pip") == 0);
+
+ npipe0(argv[1]);
+ npipe1(argv[1]);
+ npipe2(argv[1]);
+ npipe3(argv[1]);
+ npipe4(argv[1]);
+ npipe5();
+
+ return 1;
+}
+
+VOID
+npipe0(char *f)
+{
+ int rc,wfd,rfd;
+ char buf[512];
+
+ DbgPrint("npipe0:++ %s\n",f);
+ nulltouch(f);
+
+ //
+ // Open for read with NONBLOCK. Open should complete
+ // without delay.
+ //
+
+ rfd = open(f, O_RDONLY | O_NONBLOCK);
+ ASSERT(rfd != -1);
+
+ //
+ // Since there is no data in pipe, read should complete and
+ // return 0 as the byte count.
+ //
+
+ rc = read(rfd,buf,512);
+ ASSERT(rc == 0);
+
+ //
+ // Open for write with NONBLOCK. Should succeed
+ //
+
+ wfd = open(f,O_WRONLY | O_NONBLOCK);
+ ASSERT(wfd != -1);
+
+ rc = write(wfd,"Hello World\n",13);
+ ASSERT(rc == 13);
+
+ rc = read(rfd,buf,512);
+ ASSERT(rc == 13 && (strcmp(buf,"Hello World\n") == 0 ));
+
+ rc = close(rfd);
+ ASSERT(rc != -1);
+
+ rc = close(wfd);
+ ASSERT(rc != -1);
+
+ //
+ // Open for write with NONBLOCK. Should fail since read handle was
+ // closed.
+ //
+
+ wfd = open(f,O_WRONLY | O_NONBLOCK);
+ ASSERT(wfd == -1 && errno == ENXIO);
+
+ DbgPrint("npipe0:--\n");
+}
+
+VOID
+npipe1(char *f)
+{
+ int rc,wfd,rfd,stat_loc;
+ pid_t child;
+ char buf[512];
+
+ DbgPrint("npipe1:++ %s\n",f);
+
+ wfd = open("foobar.bad",O_WRONLY);
+
+ nulltouch(f);
+ child = fork();
+ nulltouch(f);
+
+ //
+ // Make sure that in the simple case,
+ // the named pipe open protocol works
+ //
+
+ if ( child == 0 ) {
+
+ rfd = open(f,O_RDONLY);
+ ASSERT(rfd != -1);
+
+ rc = read(rfd,buf,512);
+ ASSERT(rc == 13 && (strcmp(buf,"Hello World\n") == 0 ));
+
+ _exit(rc);
+ }
+
+ wfd = open(f,O_WRONLY);
+ ASSERT(wfd != -1);
+
+ rc = write(wfd,"Hello World\n",13);
+ ASSERT(rc == 13);
+
+ rc = waitpid(child,&stat_loc,0);
+ ASSERT(rc == child && WIFEXITED(stat_loc) && WEXITSTATUS(stat_loc) == 13);
+
+ rc = close(wfd);
+ ASSERT(rc != -1);
+
+ //
+ // Open for write with NONBLOCK. Should fail since read handle was
+ // closed by childs process exit.
+ //
+
+ wfd = open(f,O_WRONLY | O_NONBLOCK);
+ ASSERT(wfd == -1 && errno == ENXIO);
+
+ DbgPrint("npipe1:--\n");
+}
+
+VOID
+npipe2(char *f)
+{
+ int rc,wfd,rfd,rfd2,stat_loc;
+ pid_t child1,child2;
+ char buf[512];
+
+ DbgPrint("npipe2:++ %s\n",f);
+
+ nulltouch(f);
+ child1 = fork();
+ nulltouch(f);
+
+ //
+ // Make sure that if we have a case where two readers open the
+ // pipe, one writers open will catch them both
+ //
+
+ if ( child1 == 0 ) {
+
+ nulltouch(f);
+ child2 = fork();
+ nulltouch(f);
+
+ if ( child2 == 0 ) {
+
+ rfd2 = open(f,O_RDONLY);
+ ASSERT(rfd != -1);
+
+ rc = read(rfd2,buf,512);
+ ASSERT(rc == 13 && (strcmp(buf,"Hello World\n") == 0 ));
+
+ _exit(rc);
+ }
+
+ rfd = open(f,O_RDONLY);
+ ASSERT(rfd != -1);
+
+ rc = waitpid(child2,&stat_loc,0);
+ ASSERT(rc == child2 && WIFEXITED(stat_loc) && WEXITSTATUS(stat_loc) == 13);
+
+ _exit(WEXITSTATUS(stat_loc));
+ }
+
+ sleep(30);
+
+ wfd = open(f,O_WRONLY);
+ ASSERT(wfd != -1);
+
+ rc = write(wfd,"Hello World\n",13);
+ ASSERT(rc == 13);
+
+ rc = waitpid(child1,&stat_loc,0);
+ ASSERT(rc == child1 && WIFEXITED(stat_loc) && WEXITSTATUS(stat_loc) == 13);
+
+ rc = close(wfd);
+ ASSERT(rc != -1);
+
+ //
+ // Open for write with NONBLOCK. Should fail since read handle was
+ // closed by childs process exit.
+ //
+
+ wfd = open(f,O_WRONLY | O_NONBLOCK);
+ ASSERT(wfd == -1 && errno == ENXIO);
+
+ DbgPrint("npipe2:--\n");
+}
+
+void
+npipe3_handler(
+ IN int sig
+ )
+{
+ int wfd;
+
+ ASSERT(sig == SIGUSR1);
+ wfd = open("named.pip",O_WRONLY | O_NONBLOCK);
+ ASSERT(wfd == -1 && errno == ENXIO);
+
+}
+
+VOID
+npipe3(char *f)
+{
+ int rc,wfd,rfd,stat_loc;
+ pid_t child;
+ struct sigaction act;
+
+ DbgPrint("npipe3:++ %s\n",f);
+
+ nulltouch(f);
+ child = fork();
+ nulltouch(f);
+
+ //
+ // While child is blocked in open, terminate him with a signal
+ // and make sure everything is ok.
+ //
+
+ if ( child == 0 ) {
+
+ rfd = open(f,O_RDONLY);
+ ASSERT(FALSE);
+ }
+
+ sleep(20);
+
+ rc = kill(child,SIGKILL);
+ ASSERT(rc==0);
+
+ rc = waitpid(child,&stat_loc,0);
+ ASSERT(rc == child && WIFSIGNALED(stat_loc) && WTERMSIG(stat_loc) == SIGKILL);
+
+ wfd = open(f,O_WRONLY | O_NONBLOCK);
+ ASSERT(wfd == -1 && errno == ENXIO);
+
+ //
+ // Now Try again. This time send a signal that child catches. He should
+ // come back from his open with EINTR and the pipe should not be left
+ // open.
+ //
+
+ nulltouch(f);
+ child = fork();
+ nulltouch(f);
+
+ //
+ // While child is blocked in open, terminate him with a signal
+ // and make sure everything is ok.
+ //
+
+ act.sa_flags = 0;
+ sigfillset(&act.sa_mask);
+ act.sa_handler = npipe3_handler;
+ rc = sigaction(SIGUSR1, &act, NULL);
+ ASSERT( rc == 0 );
+
+ if ( child == 0 ) {
+
+ rfd = open(f,O_RDONLY);
+ ASSERT(rfd == -1 && errno == EINTR);
+
+ wfd = open(f,O_WRONLY | O_NONBLOCK);
+ ASSERT(wfd == -1 && errno == ENXIO);
+
+ rc = kill(getpid(),SIGKILL);
+ ASSERT(FALSE);
+
+ }
+
+ sleep(20);
+
+ rc = kill(child,SIGUSR1);
+ ASSERT(rc==0);
+
+ rc = waitpid(child,&stat_loc,0);
+ ASSERT(rc == child && WIFSIGNALED(stat_loc) && WTERMSIG(stat_loc) == SIGKILL);
+
+ wfd = open(f,O_WRONLY | O_NONBLOCK);
+ ASSERT(wfd == -1 && errno == ENXIO);
+
+ DbgPrint("npipe3:--\n");
+}
+
+VOID
+npipe4(char *f)
+{
+ int rc,rfd;
+ int fildes[2];
+ off_t off;
+
+ DbgPrint("npipe4:++ %s\n",f);
+ nulltouch(f);
+
+ //
+ // Open for read with NONBLOCK. Open should complete
+ // without delay.
+ //
+
+ rfd = open(f,O_RDONLY | O_NONBLOCK);
+ ASSERT(rfd != -1);
+
+ //
+ // lseek on named pipe should fail
+ //
+
+ off = (off_t) 1;
+ errno = 0;
+
+ off = lseek(rfd,off,SEEK_SET);
+ ASSERT(off == -1 && errno == ESPIPE);
+
+ rc = close(rfd);
+ ASSERT(rc != -1);
+
+ rc = pipe(fildes);
+ ASSERT(rc == 0);
+
+ //
+ // lseek on regular pipe should fail
+ //
+
+ off = (off_t)11;
+ errno = 0;
+
+ off = lseek(fildes[0],off,SEEK_SET);
+ ASSERT(off == -1 && errno == ESPIPE);
+
+ off = 10;
+ errno = 0;
+
+ off = lseek(fildes[1],off,SEEK_SET);
+ ASSERT(off == -1 && errno == ESPIPE);
+
+ DbgPrint("npipe4:--\n");
+}
+
+
+VOID
+npipe5()
+{
+ int rc,rfd;
+ off_t off;
+
+ DbgPrint("npipe5:++\n");
+
+ rc = mkfifo("xpipe.pip",0);
+ ASSERT(rc==0 || ( rc == -1 && errno == EEXIST ) );
+
+ if ( rc == -1 ) {
+ DbgPrint("npipe5: **** Warning Fifo Exists ****\n");
+ }
+
+ //
+ // Open for read with NONBLOCK. Open should complete
+ // without delay.
+ //
+
+ rfd = open("xpipe.pip",O_RDONLY | O_NONBLOCK);
+ ASSERT(rfd != -1);
+
+ //
+ // lseek on named pipe should fail
+ //
+
+ off = 1;
+ errno = 0;
+
+ off = lseek(rfd,off,SEEK_SET);
+ ASSERT(off == -1 && errno == ESPIPE);
+
+ rc = close(rfd);
+ ASSERT(rc != -1);
+
+ DbgPrint("npipe5:--\n");
+}
diff --git a/private/posix/client/tst/tstrmdir.c b/private/posix/client/tst/tstrmdir.c
new file mode 100644
index 000000000..f9614afb5
--- /dev/null
+++ b/private/posix/client/tst/tstrmdir.c
@@ -0,0 +1,91 @@
+#include <nt.h>
+#include <ntrtl.h>
+
+#include <signal.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "tsttmp.h" // defines DbgPrint as printf
+
+extern int errno;
+VOID rmdir0(char *);
+
+//
+// 'tstrmdir dirname'.
+//
+// The directory /psx/test is used as the base directory. It is assumed
+// to have the following sub directories:
+// rmtst1 containing one file "ab"
+// rmtst2 containing one file ".a" (??)
+// rmtst3 containing one file "a."
+// rmtst4 containing one file "abcde"
+// /psx/test must not have an existing subdirectory with the same name as
+// the dir argument.
+//
+
+int
+main(int argc, char *argv[])
+{
+
+ if (argc != 2) {
+ DbgPrint("Usage: 'tstrmdir dirname'\n");
+ return 1;
+ }
+ rmdir0(argv[1]);
+
+ return 1;
+}
+
+
+VOID
+rmdir0(char *f)
+{
+ int rc;
+
+ DbgPrint("rmdir0:++ %s\n",f);
+
+ DbgPrint("chdir to /psx/test\n");
+ rc = chdir("/psx/test");
+ ASSERT(rc != -1);
+ if (rc == -1)
+ DbgPrint("chdir errno = %d\n", errno);
+ //
+ // Test deleting an empty directory
+ //
+ DbgPrint("mkdir %s\n", f);
+ rc = mkdir(f, 0);
+ ASSERT(rc != -1);
+ if (rc == -1)
+ DbgPrint("mkdir errno = %d\n", errno);
+
+ DbgPrint("Testing removal of empty directory %s\n", f);
+ rc = rmdir(f);
+ ASSERT(rc != -1);
+ if (rc == -1)
+ DbgPrint("rmdir errno = %d\n", errno);
+
+ DbgPrint("Testing removal of nonexistent directory %s\n", f);
+ rc = rmdir(f);
+ ASSERT(rc == -1 && errno == ENOENT);
+
+ DbgPrint("Testing removal of 'rmtst1' - with one entry 'ab'\n");
+ rc = rmdir("rmtst1");
+ ASSERT(rc == -1 && errno == ENOTEMPTY);
+
+// DbgPrint("Testing removal of 'rmtst2' with one entry '.a'\n");
+// rc = rmdir("rmtst2");
+// ASSERT(rc == -1 && errno == ENOTEMPTY);
+
+ DbgPrint("Testing removal of 'rmtst3' with one entry 'a.'\n");
+ rc = rmdir("rmtst3");
+ ASSERT(rc == -1 && errno == ENOTEMPTY);
+
+ DbgPrint("Testing removal of 'rmtst4' with one entry 'abcde' \n");
+ rc = rmdir("rmtst4");
+ ASSERT(rc == -1 && errno == ENOTEMPTY);
+
+ DbgPrint("rmdir0:--\n");
+}
diff --git a/private/posix/client/tst/tstsid.c b/private/posix/client/tst/tstsid.c
new file mode 100644
index 000000000..9f2d2f3f6
--- /dev/null
+++ b/private/posix/client/tst/tstsid.c
@@ -0,0 +1,284 @@
+#include <nt.h>
+#include <ntrtl.h>
+
+#include <signal.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#include "tsttmp.h" // defines DbgPrint as printf
+
+extern int errno;
+
+VOID setsid0(VOID);
+VOID setpgid0(VOID);
+VOID kill0(VOID);
+VOID waitpid0(VOID);
+
+int _CRTAPI1 main(int argc, char *argv[])
+{
+
+ pid_t self;
+ PCH p,t;
+ PTEB ThreadInfo;
+
+ ThreadInfo = NtCurrentTeb();
+
+ self = getpid();
+
+ DbgPrint("setsidt: My pid is %lx Argc = %lx\n",self,argc);
+ DbgPrint("setsidt: StackBase %lx\n",ThreadInfo->NtTib.StackBase);
+ DbgPrint("setsidt: StackLimit %lx\n",ThreadInfo->NtTib.StackLimit);
+ DbgPrint("setsidt: ClientId %lx.%lx\n",ThreadInfo->ClientId.UniqueProcess,ThreadInfo->ClientId.UniqueThread);
+
+ while(argc--){
+ p = *argv++;
+ t = p;
+ while(*t++);
+ DbgPrint("Argv --> %s\n",p);
+ }
+
+ setsid0();
+ setpgid0();
+ kill0();
+ waitpid0();
+
+ return 1;
+}
+
+
+VOID
+setsid0()
+{
+ pid_t pid, OrigGroup, NewGroup;
+
+ DbgPrint("setsid0:++\n");
+
+ OrigGroup = getpgrp();
+
+ //
+ // Should be process group leader
+ //
+
+ ASSERT(getpid() == OrigGroup);
+
+
+ NewGroup = setsid();
+
+ ASSERT(NewGroup == -1 && errno == EPERM);
+
+ //
+ // Fork. Child then creates a new session id
+ //
+
+ if ( !fork() ) {
+
+ pid = getpid();
+
+ ASSERT(getpgrp() == OrigGroup);
+
+ ASSERT(pid != OrigGroup);
+
+ NewGroup = setsid();
+
+ ASSERT(NewGroup == pid);
+
+ ASSERT(getpgrp() == pid);
+
+ _exit(1);
+
+ }
+
+ wait(NULL);
+
+ DbgPrint("setsid0:--\n");
+}
+
+
+VOID
+setpgid0()
+{
+ pid_t OrigGroup, child;
+ int rc;
+
+ DbgPrint("setpgid0:++\n");
+
+ OrigGroup = getpgrp();
+
+ //
+ // Bad pid gives EINVAL
+ //
+
+ rc = setpgid(-1,0);
+
+ ASSERT(rc == -1 && errno == EINVAL);
+
+ //
+ // Bogus pid gives ESRCH
+ //
+
+ rc = setpgid(1,0);
+
+ ASSERT(rc == -1 && errno == ESRCH);
+
+ //
+ // Self (at this level gives EPERM because I am a session leader)
+ //
+
+ rc = setpgid(0,0);
+
+ ASSERT(rc == -1 && errno == EPERM);
+
+ child = fork();
+
+ if ( !child) {
+ child = fork();
+ if ( !child ) {
+ pause();
+ }
+
+ //
+ // Make sure child is not in same session as caller. Then try to
+ // set it's group id.
+ //
+
+ setsid();
+ rc = setpgid(child,0);
+
+ ASSERT(rc == -1 && errno == EPERM);
+
+ kill(child,SIGKILL);
+ wait(NULL);
+ _exit(2);
+ }
+
+ wait(NULL);
+
+ DbgPrint("setpgid0:--\n");
+}
+
+
+VOID
+kill0()
+{
+ pid_t parent, parentgroup, OrigGroup, child;
+ int rc;
+
+ DbgPrint("kill0:++\n");
+
+ OrigGroup = getpgrp();
+
+ child = fork();
+
+ if ( !child) {
+
+ //
+ // Change to a new process group
+ //
+
+ rc = setpgid(0,0);
+
+ ASSERT(rc == 0);
+
+ child = fork();
+
+
+ if ( !child ) {
+
+ struct sigaction act;
+
+ act.sa_handler = SIG_IGN;
+
+ rc = sigaction(SIGHUP, &act, NULL);
+
+ ASSERT( rc == 0 );
+
+ parentgroup = getpgrp();
+
+ //
+ // Change to a new process group
+ //
+
+ rc = setpgid(0,0);
+
+ ASSERT(rc == 0);
+
+ //
+ // Kill Parent by process group
+ //
+
+ parent = getppid();
+
+ rc = kill(-1 * parentgroup,SIGKILL);
+
+ ASSERT(rc == 0 && getppid() != parent );
+
+ _exit(1);
+ }
+
+ DbgPrint("kill0: Pid to die %lx Child (that will killed) %lx\n",getpid(),child);
+
+ pause();
+ }
+
+ DbgPrint("kill0: Pid %lx Child (to be killed) %lx\n",getpid(),child);
+
+ wait(NULL);
+ sleep(4);
+
+ DbgPrint("kill0:--\n");
+}
+
+VOID
+waitpid0()
+{
+ pid_t child;
+ int rc;
+
+ DbgPrint("waitpid0:++\n");
+
+ //
+ // Test for existing group with no children
+ //
+
+ rc = waitpid(0,NULL,0);
+
+ ASSERT(rc == -1 && errno == ECHILD);
+
+ //
+ // Test for non-existing group with no children
+ //
+
+ rc = waitpid(0x12345678,NULL,0);
+
+ ASSERT(rc == -1 && errno == ECHILD);
+
+ //
+ // Test for bad options
+ //
+
+ rc = waitpid(0,NULL,0x12345678);
+
+ ASSERT(rc == -1 && errno == EINVAL);
+
+ child = fork();
+
+ if ( !child) {
+ _exit(1);
+ }
+ sleep(5);
+
+ //
+ // test for specific pid
+ //
+
+ DbgPrint("waiting on %lx\n",child);
+
+ rc = waitpid(child,NULL,0);
+
+ ASSERT(rc == child);
+
+ DbgPrint("waitpid0:--\n");
+}
diff --git a/private/posix/client/tst/tstsig.c b/private/posix/client/tst/tstsig.c
new file mode 100644
index 000000000..44b066456
--- /dev/null
+++ b/private/posix/client/tst/tstsig.c
@@ -0,0 +1,120 @@
+#include <nt.h>
+#include <ntrtl.h>
+
+#include "psxmsg.h"
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <stdio.h>
+
+#include "tsttmp.h" // defines DbgPrint as printf
+
+extern int errno;
+
+void
+_CRTAPI1
+catcher(
+ IN int sig
+ );
+
+int caught_sig;
+
+int
+_CRTAPI1
+main(int argc, char *argv[])
+{
+
+ pid_t pid,cpid;
+ ULONG status;
+ LARGE_INTEGER DelayTime;
+#ifdef longtest
+ LONG i;
+ struct sigaction act, oact;
+ ULONG begin,end;
+#endif
+
+ pid = getpid();
+
+ DbgPrint("Posix Process... Pid = %lx\n\n",pid);
+
+ DelayTime.HighPart = -1;
+ DelayTime.LowPart = -500000;
+ DbgPrint("Delay\n");
+ NtDelayExecution(FALSE,&DelayTime);
+ DbgPrint("Delay Done\n");
+
+ cpid = wait(&status);
+
+ DbgPrint("hellol: wait for %lx satisfied... status %lx errno %lx\n",
+ cpid,
+ status,
+ errno
+ );
+
+ DbgPrint("hellol: waiting again. Should get ECHILD\n");
+
+ cpid = wait(&status);
+
+ DbgPrint("hellol: wait for %lx satisfied... status %lx errno %lx\n",
+ cpid,
+ status,
+ errno
+ );
+ return 0;
+
+#ifdef longtest
+
+ //
+ // try to catch SIGKILL
+ //
+
+ act.sa_handler = catcher;
+ sigfillset(&act.sa_mask);
+ act.sa_flags = 0;
+
+ if (sigaction(SIGUSR1, &act ,&oact) ) {
+ DbgPrint("main: fail sigaction errno %lx\n",errno);
+ _exit(-1);
+ }
+
+ DbgPrint("hellol: killing self\n");
+ caught_sig = 0;
+ i = kill(pid,SIGUSR1);
+ if ( !caught_sig ) {
+ DbgPrint("Error kill returned before signal handler executed\n");
+ }
+ DbgPrint("back from kill %lx\n",i);
+
+ DbgPrint("hellol: killing looper\n");
+ i = kill(0x00010001,SIGUSR1);
+ DbgPrint("back from kill %lx\n",i);
+
+ for(i=0;i<7;i++) {
+ begin = rnuminstr();
+ __NullPosixApi();
+ end = rnuminstr();
+
+ DbgPrint("Call Time bg %lx end %lx totals 0x%lx %ld \n",begin, end, end - begin,end - begin);
+ }
+
+ DbgPrint("hellol: killing looper again. Should be paused\n");
+ i = kill(0x00010001,SIGUSR1);
+ DbgPrint("back from kill %lx\n",i);
+
+ DbgPrint("Exiting...\n");
+
+ _exit(1);
+#endif
+}
+
+
+void
+_CRTAPI1
+catcher(
+ IN int sig
+ )
+{
+ DbgPrint("In Catcher, signal == %lx\n",sig);
+ caught_sig = 1;
+}
diff --git a/private/posix/client/tst/tstsum.c b/private/posix/client/tst/tstsum.c
new file mode 100644
index 000000000..13a54c1c3
--- /dev/null
+++ b/private/posix/client/tst/tstsum.c
@@ -0,0 +1,138 @@
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <unistd.h>
+#include <stdio.h>
+
+//
+// 'tstsum.c'
+// Largest sum of a subarray
+//
+// 05/14/92 DarekM created
+//
+
+int
+main(int argc, char *argv[])
+{
+ Randomize();
+
+ n1(); Results(1);
+ n2(); Results(2);
+ n3(); Results(3);
+ printf("\n\n");
+ return 1;
+}
+
+#define NUM_NUMS 20
+
+int x[NUM_NUMS];
+
+int sLargest; /* largest sum */
+int iLargest; /* index of subarray */
+int cLargest; /* size of subarray */
+
+Randomize()
+{
+ int i;
+ int s;
+
+ s = getpid();
+
+ printf("\n");
+ for (i=0; i < NUM_NUMS; i++)
+ {
+ s = (s * 89 + 13) % 47; /* generate random numbers around -25 to 25 */
+ x[i] = s - 25;
+ printf("Num[%02d] = %+d\n", i, x[i]);
+ }
+
+ printf("\n");
+}
+
+
+FindLargest(s, i, c)
+int s, i, c;
+{
+ /* takes the gives sum, index, and count of a subarray and
+ * if it is a largest sum so far keep track of it.
+ */
+
+ if ((s > sLargest) || ((s == sLargest) && (c < cLargest)))
+ {
+ sLargest = s;
+ iLargest = i;
+ cLargest = c;
+ }
+}
+
+Results(o)
+int o;
+{
+ printf("O(%d): Largest subarray is Num[%d..%d] with a sum of %d\n",
+ o, iLargest, iLargest+cLargest-1, sLargest);
+}
+
+n1()
+{
+ int i, c, s;
+
+ sLargest = -999;
+
+ s = c = 0;
+
+ for (i = 0; i < NUM_NUMS; i++)
+ {
+ if (s + x[i] < 0)
+ {
+ s = c = 0;
+ continue;
+ }
+
+ s += x[i];
+ c++;
+
+ FindLargest(s, i-c+1, c);
+ }
+}
+
+
+n2()
+{
+ int i, c, s;
+
+ sLargest = -999;
+
+ for (i = 0; i < NUM_NUMS; i++)
+ {
+ s = 0;
+
+ for (c = 1; c <= (NUM_NUMS-i); c++)
+ {
+ s += x[i+c-1];
+
+ FindLargest(s, i, c);
+ }
+ }
+}
+
+
+n3()
+{
+ int i, c, s, j;
+
+ sLargest = -999;
+
+ for (i = 0; i < NUM_NUMS; i++)
+ {
+ for (c = 1; c <= (NUM_NUMS-i); c++)
+ {
+ s = 0;
+
+ for (j = i; j < (i+c); j++)
+ s += x[j];
+
+ FindLargest(s, i, c);
+ }
+ }
+}
+
diff --git a/private/posix/client/tst/tsttime.c b/private/posix/client/tst/tsttime.c
new file mode 100644
index 000000000..0f9ed5e3b
--- /dev/null
+++ b/private/posix/client/tst/tsttime.c
@@ -0,0 +1,40 @@
+
+#include <unistd.h>
+#include <stdio.h>
+#include <malloc.h>
+#include <time.h>
+
+//
+// 'tsttime.c'
+// Time function sanity check.
+//
+// 06/10/92 DarekM Created
+//
+
+time_t loc_time;
+time_t gm_time;
+
+int
+main(int argc, char *argv[])
+{
+ int i; // this is to introduce some time delays
+
+ for (i=0; i<20000; i++)
+ loc_time = time(NULL);
+ printf("Local Time #1 = %d, %s\n", loc_time, asctime(localtime(&loc_time)));
+
+ for (i=0; i<20000; i++)
+ time(&loc_time);
+ printf("Local Time #2 = %d, %s\n", loc_time, asctime(localtime(&loc_time)));
+
+ for (i=0; i<20000; i++)
+ time(&gm_time);
+ printf("GMT Time = %d, %s\n", loc_time, asctime(gmtime(&gm_time)));
+
+ printf("Elapsed time = %d ms\n", clock());
+
+ printf("\n\n");
+ return 1;
+}
+
+
diff --git a/private/posix/client/tst/tsttmp.h b/private/posix/client/tst/tsttmp.h
new file mode 100644
index 000000000..e1a36456c
--- /dev/null
+++ b/private/posix/client/tst/tsttmp.h
@@ -0,0 +1,5 @@
+#ifdef PSX_IN_WIN
+
+#define DbgPrint printf
+
+#endif
diff --git a/private/posix/client/tst/tstumask.c b/private/posix/client/tst/tstumask.c
new file mode 100644
index 000000000..2e185ee0e
--- /dev/null
+++ b/private/posix/client/tst/tstumask.c
@@ -0,0 +1,68 @@
+#include <nt.h>
+#include <ntrtl.h>
+
+#include <signal.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#include "tsttmp.h" // defines DbgPrint as printf
+
+extern int errno;
+VOID umask0(void);
+
+//
+// 'tstumask'
+//
+
+int
+_CRTAPI1
+main(int argc, char *argv[])
+{
+
+ if (argc != 1) {
+ DbgPrint("Usage: '%s'\n", argv[0]);
+ return 1;
+ }
+ umask0();
+
+ return 1;
+}
+
+
+VOID
+umask0(void)
+{
+ mode_t oldmask, savemask;
+
+ DbgPrint("umask0:++\n");
+
+ oldmask = umask(S_IRWXU);
+ savemask = oldmask;
+ oldmask = umask(S_IRWXG);
+ if ((oldmask & S_IRWXU) != S_IRWXU) {
+ DbgPrint("FAIL on S_IRWXU\n");
+ return;
+ }
+ oldmask = umask(S_IRWXO);
+ if ((oldmask & S_IRWXG) != S_IRWXG) {
+ DbgPrint("FAIL on S_IRWXG\n");
+ return;
+ }
+ oldmask = umask((mode_t) 0L);
+ if ((oldmask & S_IRWXO) != S_IRWXO) {
+ DbgPrint("FAIL on S_IRWXO\n");
+ return;
+ }
+ oldmask = umask(savemask);
+ if ( (oldmask & _S_PROT) != (mode_t) 0L) {
+ DbgPrint("FAIL on 0 perm\n");
+ return;
+ }
+ DbgPrint("PASSED\n");
+
+ DbgPrint("umask0:--\n");
+}
diff --git a/private/posix/dirs b/private/posix/dirs
new file mode 100644
index 000000000..c738df474
--- /dev/null
+++ b/private/posix/dirs
@@ -0,0 +1,28 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+DIRS=psxss \
+ client \
+ rtl \
+ programs
+
+OPTIONAL_DIRS=
diff --git a/private/posix/filereqs.psx b/private/posix/filereqs.psx
new file mode 100644
index 000000000..1f827c7f7
--- /dev/null
+++ b/private/posix/filereqs.psx
@@ -0,0 +1,104 @@
+Gary,
+
+Here's some excerpts from the standard and the specifics for each relevant
+POSIX function. I put astericks next to statements that are of importance
+for the implementation. The situation is better than I had thought due to
+the possibility of delaying file times updates.
+
+-----------------------------------------------------
+
+POSIX Requirements for File Times Updates
+
+Each file has 3 time values that are updated when
+
+1. atime - file data has been accessed (i.e. read)
+2. mtime - file data has been modified (i.e. write)
+3. ctime - file status has been changed (i.e. chmod)
+
+For each of the functions listed below, the appropriate time-related
+fields are noted as "marked for update".
+
+**** An implementation may update fields that are marked for update
+immediately, or may update such fields periodically.****
+
+When the fields are updated, they are set to the current time and the
+update marks are cleared. All files that are marked for update shall
+be updated when the file is no longer open by any process,
+or when a stat or fstat (NtQueryInformationFile) is done. Other times
+at which updates are done are unspecified.
+
+**** Updates are not done for files on read-only file systems. ****
+
+By function:
+
+DONE
+
+readdir -
+ atime of DIRECTORY updated each time is actually read.
+ NtQueryDirectoryFile for FileNamesInformation
+
+open -
+ If O_CREAT and file didn't previously exist, update atime, ctime,
+ and mtime of FILE and PARENT DIRECTORY.
+
+ NtCreateFile -
+ DesiredAccess=SYNCHRONIZE|READ_CONTROL|FILE_READ_ATTRIBUTES|FILE_READ_EA
+ or'ed with FILE_READ_DATA, FILE_WRITE_DATA, or both, or
+ FILE_APPEND_DATA.
+ ShareAccess = FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE
+ FileAttributes = FILE_ATTRIBUTE_NORMAL.
+
+ If O_TRUNC and file did exist, update ctime and mtime of FILE.
+
+ NtOpenFile with access same as above.
+
+creat - Same as open with O_CREAT and O_TRUNC.
+
+mkdir - Update atime, ctime, and mtime of DIRECTORY; update ctime and
+ mtime of PARENT DIRECTORY.
+
+ NtCreateFile -
+ DesiredAccess = 0L; ShareAccess = 0L.
+ FileAttributes = FILE_ATTRIBUTE_NORMAL.
+
+mkfifo - Update atime, ctime, mtime of FILE. Update ctime, mtime of PARENT
+ DIRECTORY.
+
+ NtCreateFile -
+ DesiredAccess = FILE_READ_DATA; ShareAccess = 0L.
+ FileAttributes = FILE_ATTRIBUTE_SYSTEM.
+
+pipe - atime, ctime, and mtime of pipe
+ Done in psx subsystem now - may take explicit setting of times.
+
+-------------------------------------
+TO BE DONE (should be complete before December 15 - I will update at that time)
+
+link - Update ctime of FILE, ctime and mtime of PARENT DIRECTORY containing
+ new entry.
+ NtSetInformationFile
+
+unlink - Update ctime of FILE unless link count goes to 0, ctime and mtime
+ of PARENT DIRECTORY.
+ NtSetInformationFile
+
+rmdir - ctime and mtime of PARENT DIRECTORY.
+ NtSetInformationFile
+
+rename - Update ctime and mtime of PARENT DIRECTORY of each file.
+ NtSetInformationFile
+
+chmod - ctime of FILE.
+ NtSetInformationFile
+
+chown - ctime of FILE.
+ NtSetInformationFile
+
+utime - ctime of FILE
+ NtSetInformationFile
+
+read - atime of FILE
+ NtReadFile
+
+write - ctime and mtime of FILE.
+ NtWriteFile
diff --git a/private/posix/inc/depend.mak b/private/posix/inc/depend.mak
new file mode 100644
index 000000000..89954eeca
--- /dev/null
+++ b/private/posix/inc/depend.mak
@@ -0,0 +1,19 @@
+#
+# Generic dependency make file for NTOS tree. See depend.cmd in
+# \nt\private\tools.
+#
+
+all: _objects.mac _depends.mac
+
+!INCLUDE .\sources.
+
+!IFNDEF SOURCES
+!ERROR Your .\sources. file must define the SOURCES= macro
+!ENDIF
+
+_objects.mac: .\sources.
+ sed -n "/^# SOURCES definition follows/,/^# SOURCES definition ends/p" .\sources \
+ | sed -n -f \nt\private\tools\depend.sed >_objects.mac
+
+_depends.mac: .\sources.
+ includes -I..\inc -I..\..\inc -i -l -e -Sobj $(_NTDEPEND) $(SOURCES) >_depends.mac
diff --git a/private/posix/inc/psxalpha.h b/private/posix/inc/psxalpha.h
new file mode 100644
index 000000000..8d8c019bb
--- /dev/null
+++ b/private/posix/inc/psxalpha.h
@@ -0,0 +1,27 @@
+
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+Copyright (c) 1993 Digital Equipment Corporation
+
+Module Name:
+
+ psxalpha.h
+
+Abstract:
+
+ This module contains the Machine dependent definitions and macros
+ for the POSIX subsystem.
+
+Author:
+
+ Ellen Aycock-Wright (ellena) 06-Nov-1990
+
+Revision History:
+
+--*/
+
+#define PSX_FORK_RETURN 0x7777
+
+#define SetPsxForkReturn(c) \
+ (c.IntV0 = PSX_FORK_RETURN)
diff --git a/private/posix/inc/psxi386.h b/private/posix/inc/psxi386.h
new file mode 100644
index 000000000..ec9ad0745
--- /dev/null
+++ b/private/posix/inc/psxi386.h
@@ -0,0 +1,27 @@
+
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ psxi386.h
+
+Abstract:
+
+ This module contains the Machine dependent definitions and macros
+ for the POSIX subsystem.
+
+Author:
+
+ Ellen Aycock-Wright (ellena) 06-Nov-1990
+
+Revision History:
+
+--*/
+
+#define PSX_FORK_RETURN 0x7777
+
+#define SetPsxForkReturn(c) \
+ ((c).Eax = PSX_FORK_RETURN)
+
diff --git a/private/posix/inc/psxi860.h b/private/posix/inc/psxi860.h
new file mode 100644
index 000000000..0cbf3dc1a
--- /dev/null
+++ b/private/posix/inc/psxi860.h
@@ -0,0 +1,27 @@
+
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ psxi860.h
+
+Abstract:
+
+ This module contains the machine dependent definitions and macros
+ for the POSIX subsystem.
+
+Author:
+
+ Ellen Aycock-Wright (ellena) 06-Nov-1990
+
+Revision History:
+
+--*/
+
+#define PSX_FORK_RETURN 0x07777777
+
+#define SetPsxForkReturn(c) \
+ ( (c).IntR16 = PSX_FORK_RETURN )
+
diff --git a/private/posix/inc/psxmips.h b/private/posix/inc/psxmips.h
new file mode 100644
index 000000000..149dfcbbf
--- /dev/null
+++ b/private/posix/inc/psxmips.h
@@ -0,0 +1,27 @@
+
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ psxmips.h
+
+Abstract:
+
+ This module contains the Machine dependent definitions and macros
+ for the POSIX subsystem.
+
+Author:
+
+ Ellen Aycock-Wright (ellena) 06-Nov-1990
+
+Revision History:
+
+--*/
+
+#define PSX_FORK_RETURN 0x7777
+
+#define SetPsxForkReturn(c) \
+ (c.IntV0 = PSX_FORK_RETURN)
+
diff --git a/private/posix/inc/psxmsg.h b/private/posix/inc/psxmsg.h
new file mode 100644
index 000000000..dc071c80b
--- /dev/null
+++ b/private/posix/inc/psxmsg.h
@@ -0,0 +1,1000 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ psxmsg.h
+
+Abstract:
+
+ This module contains the message format used to communicate transmit
+ POSIX system services between PSX and its clients.
+
+Author:
+
+ Mark Lucovsky (markl) 14-Mar-1989
+
+Revision History:
+
+--*/
+
+#ifndef _PSXMSG_
+#define _PSXMSG_
+
+#include <nt.h>
+#include <types.h>
+#include <signal.h>
+#include <utime.h>
+#include <sys\times.h>
+
+
+//
+// Define debugging flag as false if not defined already.
+//
+
+#ifndef DBG
+#define DBG 0
+#endif
+
+
+//
+// Define IF_DEBUG macro that can be used to enable debugging code that is
+// optimized out if the debugging flag is false.
+//
+
+#if DBG
+#define IF_DEBUG if (TRUE)
+#else
+#define IF_DEBUG if (FALSE)
+#endif
+
+//
+// The following describes the connection information used for
+// posix api connections
+//
+
+typedef
+VOID
+(*PSIGNALDELIVERER) (
+ IN PCONTEXT Context,
+ IN sigset_t PreviousBlockMask,
+ IN int Signal,
+ IN _handler Handler
+ );
+
+typedef
+VOID
+(*PNULLAPICALLER) (
+ IN PCONTEXT Context
+ );
+
+//
+// SubSystemData field in PEB points to the following data structure for
+// POSIX applications. Initial contents are passed back via the connection
+// information structure when the client process connects to the POSIX
+// Emulation Subsystem server
+//
+
+typedef struct _PEB_PSX_DATA {
+ ULONG Length;
+ PVOID ClientStartAddress;
+ HANDLE SessionPortHandle;
+ PVOID SessionDataBaseAddress;
+} PEB_PSX_DATA, *PPEB_PSX_DATA;
+
+
+typedef struct _PSX_DIRECTORY_PREFIX {
+ STRING NtCurrentWorkingDirectory;
+ STRING PsxCurrentWorkingDirectory;
+ STRING PsxRoot;
+} PSX_DIRECTORY_PREFIX, *PPSX_DIRECTORY_PREFIX;
+
+typedef struct _PSX_API_CONNECTINFO {
+ PSIGNALDELIVERER SignalDeliverer;
+ PNULLAPICALLER NullApiCaller;
+ PPSX_DIRECTORY_PREFIX DirectoryPrefix;
+ PEB_PSX_DATA InitialPebPsxData;
+ ULONG SessionUniqueId;
+} PSX_API_CONNECTINFO, *PPSX_API_CONNECTINFO;
+
+#define PSXSRV_VERSION 0x100390
+
+//
+// This is only defined here instead of the obvious place because the
+// server needs to copy it from one process to another during exec.
+//
+
+typedef struct _CLIENT_OPEN_FILE {
+ BOOLEAN Open;
+ BOOLEAN FdIsConsole;
+ ULONG Flags; // descriptor flags
+} CLIENT_OPEN_FILE, *PCLIENT_OPEN_FILE;
+
+//
+// These Constants define the Posix Api Numbers
+// NOTE that the initialization of the ApiDispatch table in server\apiloop.c
+// matches this exactly.
+//
+
+typedef enum _PSXAPINUMBER {
+ PsxForkApi,
+ PsxExecApi,
+ PsxWaitPidApi,
+ PsxExitApi,
+ PsxKillApi,
+ PsxSigActionApi,
+ PsxSigProcMaskApi,
+ PsxSigPendingApi,
+ PsxSigSuspendApi,
+ PsxAlarmApi,
+ PsxGetIdsApi,
+ PsxSetUidApi,
+ PsxSetGidApi,
+ PsxGetGroupsApi,
+ PsxGetLoginApi,
+ PsxCUserIdApi,
+ PsxSetSidApi,
+ PsxSetPGroupIdApi,
+ PsxUnameApi,
+ PsxTimeApi,
+ PsxGetProcessTimesApi,
+ PsxTtyNameApi,
+ PsxIsattyApi,
+ PsxSysconfApi,
+ PsxOpenApi,
+ PsxUmaskApi,
+ PsxLinkApi,
+ PsxMkDirApi,
+ PsxMkFifoApi,
+ PsxRmDirApi,
+ PsxRenameApi,
+ PsxStatApi,
+ PsxFStatApi,
+ PsxAccessApi,
+ PsxChmodApi,
+ PsxChownApi,
+ PsxUtimeApi,
+ PsxPathConfApi,
+ PsxFPathConfApi,
+ PsxPipeApi,
+ PsxDupApi,
+ PsxDup2Api,
+ PsxCloseApi,
+ PsxReadApi,
+ PsxWriteApi,
+ PsxFcntlApi,
+ PsxLseekApi,
+ PsxTcGetAttrApi,
+ PsxTcSetAttrApi,
+ PsxTcSendBreakApi,
+ PsxTcDrainApi,
+ PsxTcFlushApi,
+ PsxTcFlowApi,
+ PsxTcGetPGrpApi,
+ PsxTcSetPGrpApi,
+ PsxGetPwUidApi,
+ PsxGetPwNamApi,
+ PsxGetGrGidApi,
+ PsxGetGrNamApi,
+ PsxUnlinkApi,
+ PsxReadDirApi,
+ PsxFtruncateApi,
+ PsxNullApi,
+
+#ifdef PSX_SOCKET
+
+ PsxSocketApi,
+ PsxAcceptApi,
+ PsxBindApi,
+ PsxConnectApi,
+ PsxGetPeerNameApi,
+ PsxGetSockNameApi,
+ PsxGetSockOptApi,
+ PsxListenApi,
+ PsxRecvApi,
+ PsxRecvFromApi,
+ PsxSendApi,
+ PsxSendToApi,
+ PsxSetSockOptApi,
+ PsxShutdownApi,
+
+#endif // PSX_SOCKET
+
+ PsxMaxApiNumber
+} PSXAPINUMBER;
+
+
+//
+// Each of the following structures define the layout of the Arguments portion
+// of the PSX_API_MSG that the Api expects.
+//
+
+//
+// PsxForkApi
+//
+//
+typedef struct _PSX_FORK_MSG {
+ IN PVOID StackBase;
+ IN PVOID StackLimit;
+ IN PVOID StackAllocationBase;
+} PSX_FORK_MSG, *PPSX_FORK_MSG;
+
+//
+// PsxExecApi
+//
+typedef struct _PSX_EXEC_MSG {
+ IN UNICODE_STRING Path;
+ IN PCHAR Args; // args + environ, in view mem
+} PSX_EXEC_MSG, *PPSX_EXEC_MSG;
+
+//
+// PsxWaitPidApi
+//
+typedef struct _PSX_WAITPID_MSG {
+ IN pid_t Pid;
+ OUT ULONG StatLocValue;
+ IN ULONG Options;
+} PSX_WAITPID_MSG, *PPSX_WAITPID_MSG;
+
+//
+// PsxExitApi
+//
+typedef struct _PSX_EXIT_MSG {
+ IN ULONG ExitStatus;
+} PSX_EXIT_MSG, *PPSX_EXIT_MSG;
+
+//
+// PsxKillApi
+//
+typedef struct _PSX_KILL_MSG {
+ IN pid_t Pid;
+ IN ULONG Sig;
+} PSX_KILL_MSG, *PPSX_KILL_MSG;
+
+//
+// PsxSigActionApi
+//
+typedef struct _PSX_SIGACTION_MSG {
+ IN ULONG Sig;
+ IN struct sigaction *ActSpecified;
+ IN struct sigaction Act;
+ IN struct sigaction *OactSpecified;
+ OUT struct sigaction Oact;
+} PSX_SIGACTION_MSG, *PPSX_SIGACTION_MSG;
+
+//
+// PsxSigProcMaskApi
+//
+typedef struct _PSX_SIGPROCMASK_MSG {
+ IN ULONG How;
+ IN sigset_t *SetSpecified;
+ IN sigset_t Set;
+ OUT sigset_t Oset;
+} PSX_SIGPROCMASK_MSG, *PPSX_SIGPROCMASK_MSG;
+
+//
+// PsxSigPendingApi
+//
+typedef struct _PSX_SIGPENDING_MSG {
+ OUT sigset_t Set;
+} PSX_SIGPENDING_MSG, *PPSX_SIGPENDING_MSG;
+
+//
+// PsxSigSuspendApi
+//
+typedef struct _PSX_SIGSUSPEND_MSG {
+ IN PVOID SigMaskSpecified;
+ IN sigset_t SigMask;
+} PSX_SIGSUSPEND_MSG, *PPSX_SIGSUSPEND_MSG;
+
+//
+// PsxAlarmApi
+//
+typedef struct _PSX_ALARM_MSG {
+ IN BOOLEAN CancelAlarm;
+ IN LARGE_INTEGER Seconds;
+ OUT LARGE_INTEGER PreviousSeconds;
+} PSX_ALARM_MSG, *PPSX_ALARM_MSG;
+
+//
+// PsxSleepApi
+//
+typedef struct _PSX_SLEEP_MSG {
+ IN ULONG Seconds;
+ OUT LARGE_INTEGER PreviousSeconds;
+} PSX_SLEEP_MSG, *PPSX_SLEEP_MSG;
+
+//
+// PsxGetIdsApi
+//
+typedef struct _PSX_GETIDS_MSG {
+ OUT pid_t Pid;
+ OUT pid_t ParentPid;
+ OUT pid_t GroupId;
+ OUT uid_t RealUid;
+ OUT uid_t EffectiveUid;
+ OUT gid_t RealGid;
+ OUT gid_t EffectiveGid;
+} PSX_GETIDS_MSG, *PPSX_GETIDS_MSG;
+
+//
+// PsxSetUidApi
+//
+typedef struct _PSX_SETUID_MSG {
+ IN uid_t Uid;
+} PSX_SETUID_MSG, *PPSX_SETUID_MSG;
+
+//
+// PsxSetGidApi
+//
+typedef struct _PSX_SETGID_MSG {
+ IN gid_t Gid;
+} PSX_SETGID_MSG, *PPSX_SETGID_MSG;
+
+//
+// PsxGetLoginApi (USES VIEW MEMORY)
+//
+typedef struct _PSX_GETLOGIN_MSG {
+ IN OUT STRING LoginName;
+} PSX_GETLOGIN_MSG, *PPSX_GETLOGIN_MSG;
+
+//
+// PsxCUserIdApi (USES VIEW MEMORY)
+//
+typedef struct _PSX_CUSERID_MSG {
+ IN OUT STRING UserName;
+} PSX_CUSERID_MSG, *PPSX_CUSERID_MSG;
+
+//
+// PsxSetSidApi
+//
+// No Arguments
+//
+
+//
+// PsxSetPGroupIdApi
+//
+typedef struct _PSX_SETPGROUPID_MSG {
+ IN pid_t Pid;
+ IN pid_t Pgid;
+} PSX_SETPGROUPID_MSG, *PPSX_SETPGROUPID_MSG;
+
+//
+// PsxUnameApi (USES VIEW MEMORY)
+//
+typedef struct _PSX_UNAME_MSG {
+ OUT struct utsname *Name;
+} PSX_UNAME_MSG, *PPSX_UNAME_MSG;
+
+//
+// PsxTimeApi
+//
+typedef struct _PSX_TIME_MSG {
+ OUT LARGE_INTEGER Time;
+} PSX_TIME_MSG, *PPSX_TIME_MSG;
+
+//
+// PsxGetProcessTimesApi
+//
+typedef struct _PSX_GETPROCESSTIMES_MSG {
+ OUT struct tms ProcessTimes;
+} PSX_GETPROCESSTIMES_MSG, *PPSX_GETPROCESSTIMES_MSG;
+
+//
+// PsxTtyNameApi (USES VIEW MEMORY)
+//
+typedef struct _PSX_TTYNAME_MSG {
+ IN LONG FileDes;
+ IN OUT STRING TtyName;
+} PSX_TTYNAME_MSG, *PPSX_TTYNAME_MSG;
+
+//
+// PsxIsattyApi
+//
+typedef struct _PSX_ISATTY_MSG {
+ IN LONG FileDes;
+ OUT ULONG Command;
+} PSX_ISATTY_MSG, *PPSX_ISATTY_MSG;
+
+//
+// PsxSysconfApi
+//
+typedef struct _PSX_SYSCONF_MSG {
+ IN ULONG Name;
+} PSX_SYSCONF_MSG, *PPSX_SYSCONF_MSG;
+
+//
+// PsxOpenApi (USES VIEW MEMORY)
+//
+typedef struct _PSX_OPEN_MSG {
+ IN UNICODE_STRING Path_U;
+ IN OUT ULONG Flags; // used as flags on input and output
+ IN OUT mode_t Mode; // used as handle value on output
+} PSX_OPEN_MSG, *PPSX_OPEN_MSG;
+
+//
+// PsxUmaskApi
+//
+typedef struct _PSX_UMASK_MSG {
+ IN mode_t Cmask;
+} PSX_UMASK_MSG, *PPSX_UMASK_MSG;
+
+//
+// PsxLinkApi (USES VIEW MEMORY)
+//
+typedef struct _PSX_LINK_MSG {
+ IN UNICODE_STRING OldName;
+ IN UNICODE_STRING NewName;
+} PSX_LINK_MSG, *PPSX_LINK_MSG;
+
+//
+// PsxMkDirApi (USES VIEW MEMORY)
+//
+typedef struct _PSX_MKDIR_MSG {
+ IN UNICODE_STRING Path_U;
+ IN mode_t Mode;
+} PSX_MKDIR_MSG, *PPSX_MKDIR_MSG;
+
+//
+// PsxMkFifoApi (USES VIEW MEMORY)
+//
+typedef struct _PSX_MKFIFO_MSG {
+ IN UNICODE_STRING Path_U;
+ IN mode_t Mode;
+} PSX_MKFIFO_MSG, *PPSX_MKFIFO_MSG;
+
+//
+// PsxRmDirApi (USES VIEW MEMORY)
+//
+typedef struct _PSX_RMDIR_MSG {
+ IN UNICODE_STRING Path_U;
+} PSX_RMDIR_MSG, *PPSX_RMDIR_MSG;
+
+//
+// PsxRenameApi (USES VIEW MEMORY)
+//
+typedef struct _PSX_RENAME_MSG {
+ IN UNICODE_STRING OldName;
+ IN UNICODE_STRING NewName;
+} PSX_RENAME_MSG, *PPSX_RENAME_MSG;
+
+//
+// PsxStatApi (USES VIEW MEMORY)
+//
+typedef struct _PSX_STAT_MSG {
+ IN UNICODE_STRING Path_U;
+ OUT struct stat *StatBuf;
+} PSX_STAT_MSG, *PPSX_STAT_MSG;
+
+//
+// PsxFStatApi
+//
+typedef struct _PSX_FSTAT_MSG {
+ IN LONG FileDes;
+ OUT struct stat *StatBuf;
+} PSX_FSTAT_MSG, *PPSX_FSTAT_MSG;
+
+//
+// PsxAccessApi (USES VIEW MEMORY)
+//
+typedef struct _PSX_ACCESS_MSG {
+ IN UNICODE_STRING Path_U;
+ IN LONG Amode;
+} PSX_ACCESS_MSG, *PPSX_ACCESS_MSG;
+
+//
+// PsxChmodApi (USES VIEW MEMORY)
+//
+typedef struct _PSX_CHMOD_MSG {
+ IN UNICODE_STRING Path_U;
+ IN mode_t Mode;
+} PSX_CHMOD_MSG, *PPSX_CHMOD_MSG;
+
+//
+// PsxChownApi (USES VIEW MEMORY)
+//
+typedef struct _PSX_CHOWN_MSG {
+ IN UNICODE_STRING Path_U;
+ IN uid_t Owner;
+ IN gid_t Group;
+} PSX_CHOWN_MSG, *PPSX_CHOWN_MSG;
+
+//
+// PsxUtimeApi (USES VIEW MEMORY)
+//
+typedef struct _PSX_UTIME_MSG {
+ IN UNICODE_STRING Path_U;
+ IN struct utimbuf *TimesSpecified;
+ IN struct utimbuf Times;
+} PSX_UTIME_MSG, *PPSX_UTIME_MSG;
+
+//
+// PsxPathConfApi (USES VIEW MEMORY)
+//
+typedef struct _PSX_PATHCONF_MSG {
+ IN UNICODE_STRING Path;
+ IN ULONG Name;
+} PSX_PATHCONF_MSG, *PPSX_PATHCONF_MSG;
+
+//
+// PsxFPathConfApi
+//
+typedef struct _PSX_FPATHCONF_MSG {
+ IN LONG FileDes;
+ IN ULONG Name;
+} PSX_FPATHCONF_MSG, *PPSX_FPATHCONF_MSG;
+
+//
+// PsxPipeApi
+//
+typedef struct _PSX_PIPE_MSG {
+ IN LONG FileDes0;
+ IN LONG FileDes1;
+} PSX_PIPE_MSG, *PPSX_PIPE_MSG;
+
+//
+// PsxDupApi
+//
+typedef struct _PSX_DUP_MSG {
+ IN LONG FileDes;
+} PSX_DUP_MSG, *PPSX_DUP_MSG;
+
+//
+// PsxDup2Api
+//
+typedef struct _PSX_DUP2_MSG {
+ OUT LONG FileDes;
+ OUT LONG FileDes2;
+} PSX_DUP2_MSG, *PPSX_DUP2_MSG;
+
+//
+// PsxCloseApi
+//
+typedef struct _PSX_CLOSE_MSG {
+ IN LONG FileDes;
+} PSX_CLOSE_MSG, *PPSX_CLOSE_MSG;
+
+//
+// PsxReadApi (USES VIEW MEMORY)
+//
+typedef struct _PSX_READ_MSG {
+ IN LONG FileDes;
+ OUT PUCHAR Buf;
+ IN LONG Nbytes;
+ ULONG Scratch1;
+ ULONG Scratch2;
+ OUT ULONG Command;
+} PSX_READ_MSG, *PPSX_READ_MSG;
+
+
+//
+// PsxReadDirApi (USES VIEW MEMORY)
+//
+typedef struct _PSX_READDIR_MSG {
+ IN LONG FileDes;
+ OUT PUCHAR Buf;
+ IN LONG Nbytes;
+ IN BOOLEAN RestartScan;
+} PSX_READDIR_MSG, *PPSX_READDIR_MSG;
+
+//
+// PsxWriteApi (USES VIEW MEMORY)
+//
+typedef struct _PSX_WRITE_MSG {
+ IN LONG FileDes;
+ IN PUCHAR Buf;
+ IN LONG Nbytes;
+ ULONG Scratch1;
+ ULONG Scratch2;
+ OUT ULONG Command;
+} PSX_WRITE_MSG, *PPSX_WRITE_MSG;
+
+//
+// Values for READ_MSG.Command and WRITE_MSG.Command
+//
+
+#define IO_COMMAND_DONE 0
+#define IO_COMMAND_DO_CONSIO 1
+
+//
+// PsxFcntlApi
+//
+typedef struct _PSX_FCNTL_MSG {
+ IN LONG FileDes;
+ IN int Command;
+ IN union {
+ struct flock *pf;
+ int i;
+ } u;
+} PSX_FCNTL_MSG, *PPSX_FCNTL_MSG;
+
+//
+// PsxLseekApi
+//
+typedef struct _PSX_LSEEK_MSG {
+ IN LONG FileDes;
+ IN LONG Whence;
+ IN off_t Offset;
+} PSX_LSEEK_MSG, *PPSX_LSEEK_MSG;
+
+//
+// PsxTcGetAttr
+//
+typedef struct _PSX_TCGETATTR_MSG {
+ IN LONG FileDes;
+ OUT struct termios *Termios;
+} PSX_TCGETATTR_MSG, *PPSX_TCGETATTR_MSG;
+
+//
+// PsxTcSetAttr
+//
+typedef struct _PSX_TCSETATTR_MSG {
+ IN LONG FileDes;
+ IN LONG OptionalActions;
+ IN struct termios *Termios;
+} PSX_TCSETATTR_MSG, *PPSX_TCSETATTR_MSG;
+
+//
+// PsxTcSendBreak
+//
+typedef struct _PSX_TCSENDBREAK_MSG {
+ IN LONG FileDes;
+ IN LONG Duration;
+} PSX_TCSENDBREAK_MSG, *PPSX_TCSENDBREAK_MSG;
+
+//
+// PsxTcDrain
+//
+typedef struct _PSX_TCDRAIN_MSG {
+ IN LONG FileDes;
+} PSX_TCDRAIN_MSG, *PPSX_TCDRAIN_MSG;
+
+//
+// PsxTcFlush
+//
+typedef struct _PSX_TCFLUSH_MSG {
+ IN LONG FileDes;
+ IN LONG QueueSelector;
+} PSX_TCFLUSH_MSG, *PPSX_TCFLUSH_MSG;
+
+//
+// PsxTcFlow
+//
+typedef struct _PSX_TCFLOW_MSG {
+ IN LONG FileDes;
+ IN LONG Action;
+} PSX_TCFLOW_MSG, *PPSX_TCFLOW_MSG;
+
+//
+// PsxTcGetPGrp
+//
+typedef struct _PSX_TCGETPGRP_MSG {
+ IN LONG FileDes;
+} PSX_TCGETPGRP_MSG, *PPSX_TCGETPGRP_MSG;
+
+//
+// PsxTcSetPGrp
+//
+typedef struct _PSX_TCSETPGRP_MSG {
+ IN LONG FileDes;
+ IN pid_t PGrpId;
+} PSX_TCSETPGRP_MSG, *PPSX_TCSETPGRP_MSG;
+
+//
+// PsxGetPwUid
+//
+typedef struct _PSX_GETPWUID_MSG {
+ IN uid_t Uid;
+ IN struct passwd *PwBuf;
+ OUT int Length;
+} PSX_GETPWUID_MSG, *PPSX_GETPWUID_MSG;
+
+//
+// PsxGetPwNam
+//
+typedef struct _PSX_GETPWNAM_MSG {
+ IN char *Name;
+ IN struct passwd *PwBuf;
+ OUT int Length;
+} PSX_GETPWNAM_MSG, *PPSX_GETPWNAM_MSG;
+
+//
+// PsxGetGrGid
+//
+typedef struct _PSX_GETGRGID_MSG {
+ IN gid_t Gid;
+ IN struct group *GrBuf;
+ OUT int Length;
+} PSX_GETGRGID_MSG, *PPSX_GETGRGID_MSG;
+
+//
+// PsxGetGrNam
+//
+typedef struct _PSX_GETGRNAM_MSG {
+ IN char *Name;
+ IN struct group *GrBuf;
+ OUT int Length;
+} PSX_GETGRNAM_MSG, *PPSX_GETGRNAM_MSG;
+
+//
+// PsxGetGroups
+//
+typedef struct _PSX_GETGROUPS_MSG {
+ IN int NGroups;
+ IN gid_t *GroupList;
+} PSX_GETGROUPS_MSG, *PPSX_GETGROUPS_MSG;
+
+//
+// PsxUnlink
+//
+typedef struct _PSX_UNLINK_MSG {
+ IN UNICODE_STRING Path_U;
+} PSX_UNLINK_MSG, *PPSX_UNLINK_MSG;
+
+//
+// PsxFtruncate
+//
+typedef struct _PSX_FTRUNCATE_MSG {
+ IN LONG FileDes;
+ IN off_t Length;
+} PSX_FTRUNCATE_MSG, *PPSX_FTRUNCATE_MSG;
+
+#ifdef PSX_SOCKET
+//
+// -------- Messages for sockets.
+//
+
+//
+// PsxSocket
+//
+
+typedef struct _PSX_SOCKET_MSG {
+ IN INT AddressFamily;
+ IN INT Type;
+ IN INT Protocol;
+} PSX_SOCKET_MSG, *PPSX_SOCKET_MSG;
+
+//
+// PsxAccept
+//
+
+typedef struct _PSX_ACCEPT_MSG {
+ IN INT Socket;
+ IN struct sockaddr *Address;
+ IN OUT INT AddressLength;
+} PSX_ACCEPT_MSG, *PPSX_ACCEPT_MSG;
+
+typedef struct _PSX_BIND_MSG {
+ IN INT Socket;
+ IN struct sockaddr *Name;
+ IN INT NameLength;
+} PSX_BIND_MSG, *PPSX_BIND_MSG;
+
+typedef struct _PSX_CONNECT_MSG {
+ IN INT Socket;
+ IN struct sockaddr *Name;
+ IN INT NameLength;
+} PSX_CONNECT_MSG, *PPSX_CONNECT_MSG;
+
+typedef struct _PSX_GETPEERNAME_MSG {
+ IN INT Socket;
+ IN struct sockaddr *Name;
+ IN OUT INT NameLength;
+} PSX_GETPEERNAME_MSG, *PPSX_GETPEERNAME_MSG;
+
+typedef struct _PSX_GETSOCKNAME_MSG {
+ IN INT Socket;
+ IN struct sockaddr *Name;
+ IN OUT INT NameLength;
+} PSX_GETSOCKNAME_MSG, *PPSX_GETSOCKNAME_MSG;
+
+typedef struct _PSX_GETSOCKOPT_MSG {
+ IN INT Socket;
+ IN INT Level;
+ IN INT OptName;
+ IN PCHAR OptVal;
+ IN OUT INT OptVal;
+} PSX_GETSOCKOPT_MSG, *PPSX_GETSOCKOPT_MSG;
+
+typedef struct _PSX_LISTEN_MSG {
+ IN INT Socket;
+ IN INT BackLog;
+} PSX_LISTEN_MSG, *PPSX_LISTEN_MSG;
+
+typedef struct _PSX_RECV_MSG {
+ IN INT Socket;
+ IN PCHAR Buffer;
+ IN INT Length;
+ IN INT Flags;
+} PSX_RECV_MSG, *PPSX_RECV_MSG;
+
+typedef struct _PSX_RECVFROM_MSG {
+ IN INT Socket;
+ IN PCHAR Buffer;
+ IN INT Length;
+ IN INT Flags;
+ IN struct sockaddr *From;
+ IN INT FromLength;
+} PSX_RECVFROM_MSG, *PPSX_RECVFROM_MSG;
+
+typdef struct _PSX_SEND_MSG {
+ IN INT Socket;
+ IN PCHAR Buffer;
+ IN INT Length;
+ IN INT Flags;
+} PSX_SEND_MSG, *PPSX_SEND_MSG;
+
+typedef struct _PSX_SENDTO_MSG {
+ IN INT Socket;
+ IN PCHAR Buffer;
+ IN INT Length;
+ IN INT Flags;
+ IN struct sockaddr *To;
+ IN INT ToLength;
+} PSX_SENDTO_MSG, *PPSX_SENDTO_MSG;
+
+typdef struct _PSX_SETSOCKOPT_MSG {
+ IN INT Socket;
+ IN INT Level;
+ IN INT OptName;
+ IN PCHAR OptVal;
+ IN INT OptLen;
+} PSX_SETSOCKOPT_MSG, *PPSX_SETSOCKOPT_MSG;
+
+typdef struct _PSX_SHUTDOWN_MSG {
+ IN INT Socket;
+ IN INT How;
+} PSX_SHUTDOWN_MSG, *PPSX_SHUTDOWN_MSG;
+
+#endif // SOCKET
+
+//
+// Each API message is overlayed on top of a PORT_MESSAGE. In addition to
+// the common PORT_MESSAGE header (SenderId..MsgInfo), the PSX API message
+// format standardizes the first few words of MsgValue. The following data
+// structure defines the standard PSX API message header. Each API overlays
+// an API specific structure on top of the ArgumentArray.
+//
+
+#define MAXPSXAPIARGS (16 - 5)
+#define MAXPSXAPIARGS_BYTES ( 4 * MAXPSXAPIARGS )
+
+//
+// Each PSX Api Message contains:
+//
+// Lpc Port Message - Fixed size and not counted in the data length
+// Psx Overhead (PSXMSGOVERHEAD)
+// *ApiPort
+// ApiNumber
+// Error
+// ReturnValue
+// DataBlock
+// Signal
+// Union Data - Per API Structure
+//
+//
+// For Lpc purposes, the TotalLength of a message is sizeof(PSX_API_MSG)
+// and the DataLength is PSXMSGOVERHEAD + the sizeof the per API structure
+//
+//
+
+#define PSXMSGOVERHEAD 24
+
+typedef struct _PSX_API_MSG {
+ PORT_MESSAGE h;
+ union {
+ PSX_API_CONNECTINFO ConnectionRequest;
+ struct {
+ struct _APIPORT *ApiPort; // Supplied by ApiDispatch
+ ULONG ApiNumber; // Supplied by client range valid by ApiDispatch
+ ULONG Error; // 0'd by ApiDispatch service sets to errno value if appropriate
+ ULONG ReturnValue; // Api Function Return Code
+ PVOID DataBlock; // Null or Pointer into message buffer
+ ULONG Signal; // Signal, if Error == EINTR
+ union {
+ PSX_FORK_MSG Fork;
+ PSX_EXEC_MSG Exec;
+ PSX_WAITPID_MSG WaitPid;
+ PSX_EXIT_MSG Exit;
+ PSX_KILL_MSG Kill;
+ PSX_SIGACTION_MSG SigAction;
+ PSX_SIGPROCMASK_MSG SigProcMask;
+ PSX_SIGPENDING_MSG SigPending;
+ PSX_SIGSUSPEND_MSG SigSuspend;
+ PSX_ALARM_MSG Alarm;
+ PSX_GETIDS_MSG GetIds;
+ PSX_SETUID_MSG SetUid;
+ PSX_SETGID_MSG SetGid;
+ PSX_GETGROUPS_MSG GetGroups;
+ PSX_GETLOGIN_MSG GetLogin;
+ PSX_CUSERID_MSG CUserId;
+ PSX_SETPGROUPID_MSG SetPGroupId;
+ PSX_UNAME_MSG Uname;
+ PSX_TIME_MSG Time;
+ PSX_GETPROCESSTIMES_MSG GetProcessTimes;
+ PSX_TTYNAME_MSG TtyName;
+ PSX_ISATTY_MSG Isatty;
+ PSX_SYSCONF_MSG Sysconf;
+ PSX_OPEN_MSG Open;
+ PSX_UMASK_MSG Umask;
+ PSX_LINK_MSG Link;
+ PSX_MKDIR_MSG MkDir;
+ PSX_MKFIFO_MSG MkFifo;
+ PSX_RMDIR_MSG RmDir;
+ PSX_RENAME_MSG Rename;
+ PSX_STAT_MSG Stat;
+ PSX_FSTAT_MSG FStat;
+ PSX_ACCESS_MSG Access;
+ PSX_CHMOD_MSG Chmod;
+ PSX_CHOWN_MSG Chown;
+ PSX_UTIME_MSG Utime;
+ PSX_PATHCONF_MSG PathConf;
+ PSX_FPATHCONF_MSG FPathConf;
+ PSX_PIPE_MSG Pipe;
+ PSX_DUP_MSG Dup;
+ PSX_DUP2_MSG Dup2;
+ PSX_CLOSE_MSG Close;
+ PSX_READ_MSG Read;
+ PSX_READDIR_MSG ReadDir;
+ PSX_WRITE_MSG Write;
+ PSX_FCNTL_MSG Fcntl;
+ PSX_LSEEK_MSG Lseek;
+ PSX_TCGETATTR_MSG TcGetAttr;
+ PSX_TCSETATTR_MSG TcSetAttr;
+ PSX_TCSENDBREAK_MSG TcSendBreak;
+ PSX_TCDRAIN_MSG TcDrain;
+ PSX_TCFLUSH_MSG TcFlush;
+ PSX_TCFLOW_MSG TcFlow;
+ PSX_TCGETPGRP_MSG TcGetPGrp;
+ PSX_TCSETPGRP_MSG TcSetPGrp;
+ PSX_GETPWUID_MSG GetPwUid;
+ PSX_GETPWNAM_MSG GetPwNam;
+ PSX_GETGRGID_MSG GetGrGid;
+ PSX_GETGRNAM_MSG GetGrNam;
+ PSX_UNLINK_MSG Unlink;
+ PSX_FTRUNCATE_MSG Ftruncate;
+
+#ifdef PSX_SOCKET
+
+ PSX_SOCKET_MSG Socket;
+ PSX_ACCEPT_MSG Accept;
+ PSX_BIND_MSG Bind;
+ PSX_CONNECT_MSG Connect;
+ PSX_GETPEERNAME_MSG GetPeerName;
+ PSX_GETSOCKNAME_MSG GetSockName;
+ PSX_GETSOCKOPT_MSG GetSockOpt;
+ PSX_LISTEN_MSG Listen;
+ PSX_RECV_MSG Recv;
+ PSX_RECVFROM_MSG RecvFrom;
+ PSX_SEND_MSG Send;
+ PSX_SENDTO_MSG SendTo;
+ PSX_SETSOCKOPT_MSG SetSockOpt;
+ PSX_SHUTDOWN_MSG Shutdown;
+
+#endif // PSX_SOCKET
+ } u;
+ };
+ };
+} PSX_API_MSG;
+typedef PSX_API_MSG *PPSX_API_MSG;
+
+#define PSX_API_MSG_LENGTH(TypeSize) \
+ sizeof(PSX_API_MSG)<<16 | (PSXMSGOVERHEAD + (TypeSize))
+
+#define PSX_FORMAT_API_MSG(m,Number,TypeSize) \
+ (m).h.u1.Length = PSX_API_MSG_LENGTH((TypeSize)); \
+ (m).h.u2.ZeroInit = 0L; \
+ (m).ApiNumber = (Number)
+
+//
+// PSX_CLIENT_PORT_MEMORY_SIZE defines how much address space should be
+// reserved for passing data to the POSIX Server. The memory is visible
+// to both the client and server processes.
+//
+
+#define PSX_CLIENT_PORT_MEMORY_SIZE 0x8000
+
+#define PSX_SS_API_PORT_NAME L"\\PSXSS\\ApiPort"
+
+#endif // _PSXMSG_
diff --git a/private/posix/inc/psxppc.h b/private/posix/inc/psxppc.h
new file mode 100644
index 000000000..db9c7bd4e
--- /dev/null
+++ b/private/posix/inc/psxppc.h
@@ -0,0 +1,27 @@
+
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ psxppc.h
+
+Abstract:
+
+ This module contains the Machine dependent definitions and macros
+ for the POSIX subsystem.
+
+Author:
+
+ Pat Carr (patcarr@pets.sps.mot.com) 23-Jun-1994
+
+Revision History:
+
+--*/
+
+#define PSX_FORK_RETURN 0x7777
+
+#define SetPsxForkReturn(c) \
+ (c.Gpr3 = PSX_FORK_RETURN)
+
diff --git a/private/posix/inc/psxsrv.h b/private/posix/inc/psxsrv.h
new file mode 100644
index 000000000..b36495bba
--- /dev/null
+++ b/private/posix/inc/psxsrv.h
@@ -0,0 +1,1876 @@
+
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ psxsrv.h
+
+Abstract:
+
+ Main include file for POSIX Subsystem Server
+
+Author:
+
+ Steve Wood (stevewo) 22-Aug-1989
+
+Revision History:
+
+ Ellen Aycock-Wright 15-Jul-91 Modify for POSIX subsystem
+--*/
+
+
+
+#ifndef _PSXP_
+#define _PSXP_
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <types.h>
+#include <string.h>
+#include <limits.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys\wait.h>
+#include <ntsm.h>
+#include "psxmsg.h"
+
+#if DBG
+#define PSX_DEBUG_INIT 0x0000001
+#define PSX_DEBUG_LPC 0x0000002
+#define PSX_DEBUG_MSGDUMP 0x0000004
+#define PSX_DEBUG_EXEC 0x0000008
+
+extern ULONG PsxDebug;
+#define IF_PSX_DEBUG(ComponentFlag ) \
+ if (PsxDebug & (PSX_DEBUG_ ## ComponentFlag))
+#else
+#define IF_PSX_DEBUG( ComponentFlag ) if (FALSE)
+#endif //DBG
+
+BOOLEAN PsxpDebuggerActive;
+HANDLE PsxpDebugPort;
+ULONG PsxpApiMsgSize;
+
+int
+__NullPosixApi();
+
+VOID
+Panic(
+ IN PSZ PanicString
+ );
+
+//
+// Constants for Posix ID / Sid mapping
+//
+
+#define SHARE_ALL (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
+
+//
+// This is the name of the directory used to store open files that have
+// been unlinked.
+//
+
+#define PSX_JUNK_DIR L"$$psxjunk"
+
+//
+// Posix Process types constants and data structures
+//
+
+#define CIDHASHSIZE 256
+
+#define CIDTOHASHINDEX(pcid) (\
+ ((ULONG)((pcid)->UniqueProcess))&(CIDHASHSIZE-1))
+
+#define PIDINDEXBITS 0xffff
+#define PIDSEQSHIFT 16
+#define SPECIALPID 1
+// #define SPECIALPID 0xff000000
+
+#define PIDTOPROCESS(pid) \
+ &FirstProcess[(pid) & PIDINDEXBITS]
+
+#define MAKEPID(pid,seq,index) \
+ (pid) = (seq) & 0xff;\
+ (pid) <<= PIDSEQSHIFT;\
+ (pid) |= ((index)&PIDINDEXBITS)
+
+#define ISFILEDESINRANGE(fd) (\
+ ( (ULONG)(fd) < OPEN_MAX ) && ( (ULONG)(fd) >= 0 ) )
+
+typedef struct _FILEDESCRIPTOR {
+ struct _SYSTEMOPENFILE *SystemOpenFileDesc;
+ ULONG Flags; // descriptor flags (FD_CLOEXEC)
+} FILEDESCRIPTOR;
+typedef FILEDESCRIPTOR *PFILEDESCRIPTOR;
+
+//
+// Flags for FILEDESCRIPTOR.Flags
+//
+#define PSX_FD_CLOSE_ON_EXEC 0x00000001
+
+typedef enum _PSX_INTERRUPTREASON {
+ SignalInterrupt,
+ WaitSatisfyInterrupt,
+ IoCompletionInterrupt,
+ SleepComplete
+ } PSX_INTERRUPTREASON;
+
+struct _PSX_PROCESS; //Make mips compiler happy
+struct _INTCB;
+
+typedef void (* INTHANDLER)(
+ IN struct _PSX_PROCESS *p,
+ IN struct _INTCB *IntControlBlock,
+ IN PSX_INTERRUPTREASON InterruptReason,
+ IN int Signal
+ );
+
+typedef struct _INTCB {
+ INTHANDLER IntHandler;
+ PPSX_API_MSG IntMessage;
+ PVOID IntContext;
+ LIST_ENTRY Links;
+} INTCB, *PINTCB;
+
+#define _SIGNULLSET 0x0
+#define _SIGFULLSET 0x7ffff // ((1<<SIGTTOU) - 1)
+#define _SIGMAXSIGNO SIGTTOU
+#define _SIGSTOPSIGNALS ((1l<<SIGSTOP) | (1l<<SIGTSTP) | (1l<<SIGTTIN)| (1l<<SIGTTOU) )
+
+#define SIGEMPTYSET(set) \
+ *(set) = _SIGNULLSET
+
+#define SIGFILLSET(set) \
+ *(set) = _SIGFULLSET
+
+#define SIGADDSET(set, signo) \
+ *(set) |= ( (1l << (ULONG)((signo)-1)) & _SIGFULLSET )
+
+#define SIGDELSET(set, signo) \
+ *(set) &= ~( (1l << (ULONG)((signo)-1)) & _SIGFULLSET )
+
+#define SIGISMEMBER(set, signo) \
+ ( *(set) & ( (1l << (ULONG)((signo)-1)) & _SIGFULLSET ) )
+
+#define ISSIGNOINRANGE(signo) \
+ ( (signo) <= _SIGMAXSIGNO )
+
+
+typedef struct sigaction SIGACTION;
+typedef SIGACTION *PSIGACTION;
+
+//
+// Each signal has an associated signal disposition.
+// when a handler is dispatched, the blocked signal mask
+// of the process is saved (as part of signal dispatch), and
+// a new mask is calculated by oring in the BlockMask bits. When
+// (if) the signal handler returns, the previous block mask is restored.
+//
+
+typedef struct sigaction SIGDISP;
+typedef SIGDISP *PSIGDISP;
+
+//
+// Each process has a signal database. The process lock of the
+// owning process must be held to change information in the
+// signal database
+//
+
+typedef struct _SIGDB {
+ sigset_t BlockedSignalMask;
+ sigset_t PendingSignalMask;
+ sigset_t SigSuspendMask;
+ SIGDISP SignalDisposition[_SIGMAXSIGNO];
+} SIGDB;
+typedef SIGDB *PSIGDB;
+
+
+typedef enum _PSX_PROCESSSTATE {
+ Unconnected,
+ Active,
+ Stopped,
+ Exited,
+ Waiting
+ } PSX_PROCESSSTATE;
+
+typedef struct _PSX_CONTROLLING_TTY {
+ LIST_ENTRY Links;
+ RTL_CRITICAL_SECTION Lock;
+
+ //
+ // There is a reference to this terminal for every file descriptor
+ // that is open on it and every session that has it as a controlling
+ // tty.
+
+ ULONG ReferenceCount;
+ pid_t ForegroundProcessGroup;
+ HANDLE ConsolePort;
+ HANDLE ConsoleCommPort;
+ ULONG UniqueId;
+ PVOID IoBuffer; // mapped in server addr space
+ struct _PSX_SESSION *Session;
+} PSX_CONTROLLING_TTY, *PPSX_CONTROLLING_TTY;
+
+typedef struct _PSX_SESSION {
+ ULONG ReferenceCount;
+ PPSX_CONTROLLING_TTY Terminal;
+ pid_t SessionLeader;
+} PSX_SESSION, *PPSX_SESSION;
+
+#define IS_DIRECTORY_PREFIX_REMOTE(p) ( (ULONG)(p) & 0x1 )
+#define MAKE_DIRECTORY_PREFIX_REMOTE(p) ( (PPSX_DIRECTORY_PREFIX)((ULONG)(p) | 0x1) )
+#define MAKE_DIRECTORY_PREFIX_VALID(p) ( (PPSX_DIRECTORY_PREFIX)((ULONG)(p) & ~0x1) )
+
+typedef struct _PSX_PROCESS {
+ LIST_ENTRY ClientIdHashLinks;
+
+ ULONG Flags;
+
+ HANDLE Process;
+ HANDLE Thread;
+ HANDLE AlarmTimer;
+
+ PSIGNALDELIVERER SignalDeliverer;
+ PNULLAPICALLER NullApiCaller;
+ PPSX_DIRECTORY_PREFIX DirectoryPrefix;
+
+ ULONG ExitStatus;
+ mode_t FileModeCreationMask;
+ FILEDESCRIPTOR ProcessFileTable[OPEN_MAX];
+ RTL_CRITICAL_SECTION ProcessLock;
+ PSX_PROCESSSTATE State;
+
+ //
+ // InPsx is a count of the number of times *this* process is in
+ // the subsystem. For instance, if he calls sigsuspend and blocks,
+ // the count should be 1. If he then executes a signal handler as
+ // a result of a signal, and the signal handler makes a syscall, the
+ // count gets bumped to 2.
+ //
+
+ ULONG InPsx;
+
+ PINTCB IntControlBlock;
+ ULONG SequenceNumber;
+
+ uid_t EffectiveUid;
+ uid_t RealUid;
+
+ gid_t EffectiveGid;
+ gid_t RealGid;
+
+ pid_t Pid;
+ pid_t ParentPid;
+ pid_t ProcessGroupId;
+ LIST_ENTRY GroupLinks;
+ PPSX_SESSION PsxSession;
+
+ HANDLE ClientPort;
+ PCH ClientViewBase;
+ PCH ClientViewBounds;
+ CLIENT_ID ClientId;
+ PEB_PSX_DATA InitialPebPsxData;
+
+ BOOLEAN ProcessIsBeingDebugged;
+ CLIENT_ID DebugUiClientId;
+
+ SIGDB SignalDataBase;
+
+ struct tms ProcessTimes;
+
+ //
+ // When the posix server needs to make a call on behalf of a client
+ // that could block, we create a thread to endure the blocking for
+ // us. This happens when we've execed a windows program and we want
+ // to know when he exits, and for some blocking sockets calls.
+ //
+
+ HANDLE BlockingThread;
+
+} PSX_PROCESS;
+typedef PSX_PROCESS *PPSX_PROCESS;
+
+//
+// Valid bits for the PSX_PROCESS 'Flags' word:
+//
+
+#define P_FREE 0x00000001 // process slot is free
+#define P_HAS_EXECED 0x00000002 // process successfully execed
+#define P_FOREIGN_EXEC 0x00000004 // this is a windows program
+#define P_SOCKET_BLOCK 0x00000008 // called blocking sockets call
+#define P_WAITED 0x00000010 // process has been waited on
+#define P_NO_FORK 0x00000020 // process fork forbidden
+
+#define AcquireProcessLock( p ) RtlEnterCriticalSection( &(p)->ProcessLock )
+#define ReleaseProcessLock( p ) RtlLeaveCriticalSection( &(p)->ProcessLock )
+
+//
+// Process is stoppable if it is not part of an orphaned process group.
+//
+
+#define ISPROCESSSTOPABLE(p) (TRUE)
+
+#define ISPOINTERVALID_CLIENT(pprocess, p, length) \
+ (((ULONG)(p) >= (ULONG)(pprocess)->ClientViewBase) && \
+ ((char *)(p) + (length)) < (pprocess)->ClientViewBounds)
+
+
+LIST_ENTRY DefaultBlockList;
+RTL_CRITICAL_SECTION BlockLock;
+
+//
+// The process Table.
+//
+// This table contains the array of processes. The array is dynamically
+// allocated at PSX initialization time. very few operations use a table scan
+// to locate processes. Since pids direct map to a process, and since ClientIds
+// hash to a process, table scans are only needed to locate groups of processes.
+//
+// A single lock (PsxProcessStructureLock) guards the process table and
+// ClientIdHashTable. This lock is needed to add or delete an entry in the
+// PsxProcessTable or ClientIdHashTable.
+//
+
+PSX_PROCESS PsxProcessTable[32];
+PPSX_PROCESS FirstProcess,LastProcess;
+
+//
+// Client Id Hash Table.
+//
+// Given a client id, the corresponding process is located by indexing into this
+// table and then searching the list head at the index.
+//
+
+LIST_ENTRY ClientIdHashTable[CIDHASHSIZE];
+
+RTL_CRITICAL_SECTION PsxProcessStructureLock;
+#define AcquireProcessStructureLock() RtlEnterCriticalSection( &PsxProcessStructureLock )
+#define ReleaseProcessStructureLock() RtlLeaveCriticalSection( &PsxProcessStructureLock )
+
+typedef struct _PSX_EXEC_INFO {
+ ANSI_STRING Path;
+ ANSI_STRING CWD;
+ ANSI_STRING Argv;
+ ANSI_STRING Envp;
+ ANSI_STRING LibPath;
+} PSX_EXEC_INFO;
+typedef PSX_EXEC_INFO *PPSX_EXEC_INFO;
+
+//
+// Routines defined in process.c
+//
+
+NTSTATUS
+PsxInitializeProcess(
+ IN PPSX_PROCESS NewProcess,
+ IN PPSX_PROCESS ForkProcess OPTIONAL,
+ IN ULONG SessionId,
+ IN HANDLE ProcessHandle,
+ IN HANDLE ThreadHandle,
+ IN PPSX_SESSION Session OPTIONAL
+ );
+
+NTSTATUS
+PsxInitializeProcessStructure( VOID );
+
+PPSX_PROCESS
+PsxAllocateProcess (
+ IN PCLIENT_ID ClientId
+ );
+
+BOOLEAN
+PsxCreateProcess(
+ IN PPSX_EXEC_INFO ExecInfo,
+ OUT PPSX_PROCESS *NewProcess,
+ IN HANDLE ParentProc,
+ IN PPSX_SESSION Session
+ );
+
+PPSX_PROCESS
+PsxLocateProcessByClientId (
+ IN PCLIENT_ID ClientId
+ );
+
+PPSX_PROCESS
+PsxLocateProcessBySession(
+ IN PPSX_SESSION Session
+ );
+
+PPSX_SESSION
+PsxLocateSessionByUniqueId(
+ IN ULONG UniqueId
+ );
+
+BOOLEAN
+PsxStopProcess(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m,
+ IN ULONG Signal,
+ IN sigset_t *RestoreBlockSigset OPTIONAL
+ );
+
+VOID
+PsxStopProcessHandler(
+ IN PPSX_PROCESS p,
+ IN PINTCB IntControlBlock,
+ IN PSX_INTERRUPTREASON InterruptReason,
+ IN int Signal
+ );
+
+VOID
+PsxInitializeDirectories(
+ IN PPSX_PROCESS Process,
+ IN PANSI_STRING pCwd
+ );
+
+BOOLEAN
+PsxPropagateDirectories(
+ IN PPSX_PROCESS Process
+ );
+
+//
+// Routines defined in procblk.c
+//
+
+NTSTATUS
+BlockProcess(
+ IN PPSX_PROCESS p,
+ IN PVOID Context,
+ IN INTHANDLER Handler,
+ IN PPSX_API_MSG m,
+ IN PLIST_ENTRY BlockList OPTIONAL,
+ IN PRTL_CRITICAL_SECTION CriticalSectionToRelease OPTIONAL
+ );
+
+BOOLEAN
+UnblockProcess(
+ IN PPSX_PROCESS p,
+ IN PSX_INTERRUPTREASON InterruptReason,
+ IN BOOLEAN BlockLockHeld,
+ IN int Signal // or 0 if not awakened by signal
+ );
+
+//
+// Routines defined in srvinit.c
+//
+
+NTSTATUS
+PsxInitializeIO( VOID );
+
+//
+// Routines defined in sigsup.c
+//
+
+int
+PsxCheckPendingSignals(
+ IN PPSX_PROCESS p
+ );
+
+VOID
+PsxTerminateProcessBySignal(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m,
+ IN ULONG Signal
+ );
+
+VOID
+PsxDeliverSignal(
+ IN PPSX_PROCESS p,
+ IN ULONG Signal,
+ IN sigset_t *RestoreBlockSigset OPTIONAL
+ );
+
+VOID
+PsxSigSuspendHandler(
+ IN PPSX_PROCESS p,
+ IN PINTCB IntControlBlock,
+ IN PSX_INTERRUPTREASON InterruptReason,
+ IN int Signal
+ );
+
+VOID
+PsxSignalProcess(
+ IN PPSX_PROCESS p,
+ IN ULONG Signal
+ );
+
+VOID
+Exit (
+ IN PPSX_PROCESS p,
+ IN ULONG ExitStatus
+ );
+
+//
+// Psx Session Primitives
+//
+
+PPSX_SESSION
+PsxAllocateSession(
+ IN PPSX_CONTROLLING_TTY Terminal OPTIONAL,
+ IN pid_t SessionLeader
+ );
+
+VOID
+PsxDeallocateSession(
+ IN PPSX_SESSION Session
+ );
+
+//
+// Psx Session Macros
+//
+
+#define REFERENCE_PSX_SESSION(ppsxsession) \
+ RtlEnterCriticalSection(&PsxNtSessionLock); \
+ (ppsxsession)->ReferenceCount++; \
+ RtlLeaveCriticalSection(&PsxNtSessionLock)
+
+//
+// Dereferences the session. If reference count goes to zero, the session
+// is deallocated.
+//
+
+#define DEREFERENCE_PSX_SESSION(ppsxsession, status) \
+ RtlEnterCriticalSection(&PsxNtSessionLock); \
+ if (--((ppsxsession)->ReferenceCount) == 0) { \
+ PsxTerminateConSession((ppsxsession), (status));\
+ PsxDeallocateSession((ppsxsession)); \
+ } else { \
+ RtlLeaveCriticalSection(&PsxNtSessionLock); \
+ }
+
+
+//
+// Routines defined in sbapi.c
+//
+
+BOOLEAN
+PsxSbCreateSession(
+ IN PSBAPIMSG Msg
+ );
+
+BOOLEAN
+PsxSbTerminateSession(
+ IN PSBAPIMSG Msg
+ );
+
+BOOLEAN
+PsxSbForeignSessionComplete(
+ IN PSBAPIMSG Msg
+ );
+
+
+//
+// Routines defined in sbinit.c
+//
+
+NTSTATUS PsxSbApiPortInitialize ( VOID );
+
+//
+// Routines defined in sbreqst.c
+//
+
+typedef BOOLEAN (*PSB_API_ROUTINE)( IN PSBAPIMSG SbApiMsg );
+
+NTSTATUS
+PsxSbApiRequestThread(
+ IN PVOID Parameter
+ );
+
+//
+// Routines defined in coninit.c
+//
+
+NTSTATUS
+PsxInitializeConsolePort( VOID );
+
+
+//
+// Routines defined in conthrds.c
+//
+
+NTSTATUS
+PsxSessionRequestThread(
+ IN PVOID Parameter
+ );
+
+
+//
+// Routines defined in concreat.c
+//
+
+VOID SetDefaultLibPath(
+ OUT PANSI_STRING LibPath,
+ IN PCHAR EnvStrings
+ );
+
+NTSTATUS
+PsxCreateConSession(
+ IN OUT PVOID RequestMsg
+ );
+
+//
+// Routines defined in consignl.c
+//
+
+NTSTATUS
+PsxCtrlSignalHandler(
+ IN OUT PVOID RequestMsg
+ );
+
+NTSTATUS
+PsxTerminateConSession(
+ IN PPSX_SESSION Session,
+ IN ULONG ExitStatus
+ );
+
+//
+// Routines defined in session.c
+//
+
+RTL_CRITICAL_SECTION PsxNtSessionLock;
+
+#define LockNtSessionList() RtlEnterCriticalSection( &PsxNtSessionLock )
+#define UnlockNtSessionList() RtlLeaveCriticalSection( &PsxNtSessionLock )
+
+NTSTATUS
+PsxInitializeNtSessionList( VOID );
+
+//
+// Routines defined in apiinit.c
+//
+
+NTSTATUS PsxApiPortInitialize ( VOID );
+
+//
+// Global data accessed by Client-Server Runtime Server
+//
+
+PVOID PsxHeap;
+
+#define PSX_SS_ROOT_OBJECT_DIRECTORY L"\\PSXSS"
+#define PSX_SS_SBAPI_PORT_NAME L"\\PSXSS\\SbApiPort"
+
+UNICODE_STRING PsxApiPortName;
+ANSI_STRING PsxSbApiPortName;
+UNICODE_STRING PsxSbApiPortName_U;
+
+STRING PsxRootDirectoryName;
+HANDLE PsxRootDirectory;
+
+HANDLE PsxApiPort;
+HANDLE PsxSbApiPort;
+HANDLE PsxSmApiPort;
+
+ULONG PsxNumberApiRequestThreads;
+
+/*
+ * Port and threads for Console Session globals.
+ */
+HANDLE PsxSessionPort;
+
+HANDLE PsxSessionRequestThreadHandle;
+
+#define PSX_SS_SBAPI_REQUEST_THREAD 0
+#define PSX_SS_FIRST_API_REQUEST_THREAD 1
+
+#define PSX_SS_MAX_THREADS 64
+
+HANDLE PsxServerThreadHandles[ PSX_SS_MAX_THREADS ];
+CLIENT_ID PsxServerThreadClientIds[ PSX_SS_MAX_THREADS ];
+
+//
+// file descriptor IO types constants and data structures
+//
+
+#define IONODEHASHSIZE 256
+
+#define SERIALNUMBERTOHASHINDEX(DeviceSerialNumber,FileSerialNumber) (\
+ (FileSerialNumber) & (IONODEHASHSIZE-1) )
+
+//
+// IoRoutines
+//
+
+//
+// This function is called when a new handle is created as a result of
+// an open
+//
+
+typedef
+BOOLEAN
+(*PPSXOPEN_NEW_HANDLE_ROUTINE) (
+ IN PPSX_PROCESS p,
+ IN PFILEDESCRIPTOR Fd,
+ IN PPSX_API_MSG m
+ );
+
+//
+// This function is called when a new handle is created as a result of
+// a pipe, dup, or fork
+//
+
+typedef
+VOID
+(*PPSXNEW_HANDLE_ROUTINE) (
+ IN PPSX_PROCESS p,
+ IN PFILEDESCRIPTOR Fd
+ );
+
+//
+// This function is called whever a handle is closed (close, exec, _exit)
+//
+
+typedef
+VOID
+(*PPSXCLOSE_ROUTINE) (
+ IN PPSX_PROCESS p,
+ IN PFILEDESCRIPTOR Fd
+ );
+
+typedef
+VOID
+(*PPSXLAST_CLOSE_ROUTINE) (
+ IN PPSX_PROCESS p,
+ IN struct _SYSTEMOPENFILE *SystemOpenFile
+ );
+
+struct _IONODE;
+typedef
+VOID
+(*PPSXIONODE_CLOSE_ROUTINE) (
+ IN struct _IONODE *IoNode
+ );
+
+//
+// This function is called to do a read
+//
+
+typedef
+BOOLEAN
+(*PPSXREAD_ROUTINE) (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd
+ );
+
+//
+// This function is called to do a write
+//
+
+typedef
+BOOLEAN
+(*PPSXWRITE_ROUTINE) (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd
+ );
+
+//
+// This function is called to do a dup or dup2
+//
+
+typedef
+BOOLEAN
+(*PPSXDUP_ROUTINE) (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd,
+ IN PFILEDESCRIPTOR FdDup
+ );
+
+//
+// This function is called to do a Lseek
+//
+
+typedef
+BOOLEAN
+(*PPSXLSEEK_ROUTINE) (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd
+ );
+
+//
+// This function is called to fill in an Ionode so that a call to
+// stat or fstat can be completed.
+//
+
+typedef
+BOOLEAN
+(*PPSXSTAT_ROUTINE) (
+ IN struct _IONODE *IoNode,
+ IN HANDLE FileHandle,
+ OUT struct stat *StatBuf,
+ OUT NTSTATUS *pStatus
+ );
+
+typedef struct _PSXIO_VECTORS {
+ PPSXOPEN_NEW_HANDLE_ROUTINE OpenNewHandleRoutine;
+ PPSXNEW_HANDLE_ROUTINE NewHandleRoutine;
+ PPSXCLOSE_ROUTINE CloseRoutine;
+ PPSXLAST_CLOSE_ROUTINE LastCloseRoutine;
+ PPSXIONODE_CLOSE_ROUTINE IoNodeCloseRoutine;
+ PPSXREAD_ROUTINE ReadRoutine;
+ PPSXWRITE_ROUTINE WriteRoutine;
+ PPSXDUP_ROUTINE DupRoutine;
+ PPSXLSEEK_ROUTINE LseekRoutine;
+ PPSXSTAT_ROUTINE StatRoutine;
+} PSXIO_VECTORS, *PPSXIO_VECTORS;
+
+BOOLEAN
+IoOpenNewHandle (
+ IN PPSX_PROCESS p,
+ IN PFILEDESCRIPTOR Fd,
+ IN PPSX_API_MSG m
+ );
+
+VOID
+IoNewHandle (
+ IN PPSX_PROCESS p,
+ IN PFILEDESCRIPTOR Fd
+ );
+
+VOID
+IoClose (
+ IN PPSX_PROCESS p,
+ IN PFILEDESCRIPTOR Fd
+ );
+
+VOID
+IoLastClose (
+ IN PPSX_PROCESS p,
+ IN struct _SYSTEMOPENFILE *SystemOpenFile
+ );
+
+//
+// Each unique open file in the system results in the creation of an Input
+// Output Node (IONODE). Multiple opens in the same file may result in
+// different file descriptors and system open files, but as long as they refer
+// to the same file, only one IONODE will be created. IONODES track the the
+// status of a file and keep track of its owner, mode, times. A single lock
+// (IoNodeLock) guards all reference count operations on IONODES. A hash table
+// IoNodeHashTable is capable of locating an IONODE based on the device and
+// inode number of the file the IONODE is refered to. IONODEs are created on
+// file open. Once the file is opened, a query of the file is made to
+// determine its device and inode number. An IONODE is searched for in the
+// InNodeHashTable, if one is found, then its reference count is incremented,
+// otherwise one is created and initialized.
+//
+
+typedef struct _IONODE {
+ LIST_ENTRY IoNodeHashLinks;
+ RTL_CRITICAL_SECTION IoNodeLock;
+ ULONG ReferenceCount;
+
+ //
+ // The file mode is created during file open.
+ // The protection portion of the mode is created by reading the file's
+ // SECURITY_DESCRIPTOR and collapsing it into POSIX rwxrwxrwx values.
+ //
+ // The file type portion is created using the file attributes, device type,
+ // and extended attributes of a file
+ //
+
+ mode_t Mode;
+
+ //
+ // For regular files,
+ // DeviceSerialNumber == Counter. Per filesystem number that does
+ // not conflict w/ device type.
+ // FileSerialNumber == IndexNumber
+ // For device files
+ // DeviceSerialNumber == DeviceType
+ // FileSerialNumber == Device Object Address (IndexNumber ?)
+ //
+
+ dev_t DeviceSerialNumber;
+ ino_t FileSerialNumber;
+
+ uid_t OwnerId;
+ gid_t GroupId;
+
+ //
+ // The time fields are magical. When the file is opened, the access time
+ // and modified times returned from the file system are stored in the
+ // IONODE. The change time is set equal to the modified time. Each Psx
+ // IONODE operation sets the appropriate time fields.
+ //
+
+ time_t AccessDataTime;
+ time_t ModifyDataTime;
+ time_t ModifyIoNodeTime;
+
+ //
+ // The Io Vectors
+ //
+
+ PVOID Context;
+ PPSXIO_VECTORS IoVectors;
+
+ //
+ // File record locks, sorted by the starting position of the lock.
+ // See fcntl(). And a list of those processes blocked while locking
+ // a region (with F_SETLKW).
+ //
+ LIST_ENTRY Flocks;
+ LIST_ENTRY Waiters;
+
+ // Length of path to ionode, if applicable. For fpathconf(PATH_MAX).
+
+ ULONG PathLength;
+
+ //
+ // If the file has been unlinked or renamed over while still open,
+ // we move it to the junkyard and set the "junked" flag. When the
+ // last close occurs for this file, it should be deleted.
+ //
+
+ BOOLEAN Junked;
+
+} IONODE, *PIONODE;
+
+//
+// Each unique open of a file results in the creation of a system open file.
+// These descriptors are dynamically allocated. There is no limit on the number
+// of these in the system. Dups of file descriptors, or forking result in a
+// reference count increment, not a new system open file. Only explicit opens,
+// creates, or pipe() calls result in a new system open file. A global lock
+// (SystemOpenFileLock) guards all reference count adjustments. As long as a
+// reference exists, all fields in this structure can be used without any
+// locking.
+//
+typedef struct _SYSTEMOPENFILE {
+ ULONG HandleCount;
+ ULONG ReadHandleCount;
+ ULONG WriteHandleCount;
+ HANDLE NtIoHandle;
+ PIONODE IoNode;
+ ULONG Flags; // file description flags
+
+ //
+ // If a file descriptor is open on a console, we need to keep a
+ // reference to that console so we can do io even if the process
+ // changes sessions. 'Terminal' is the reference.
+ //
+
+ PPSX_CONTROLLING_TTY Terminal;
+
+} SYSTEMOPENFILE;
+typedef SYSTEMOPENFILE *PSYSTEMOPENFILE;
+
+//
+// Flags for SYSTEMOPENFILE.Flags
+//
+#define PSX_FD_READ 0x00000001
+#define PSX_FD_WRITE 0x00000002
+#define PSX_FD_NOBLOCK 0x00000004
+#define PSX_FD_APPEND 0x00000008
+
+extern RTL_CRITICAL_SECTION SystemOpenFileLock;
+extern RTL_CRITICAL_SECTION IoNodeHashTableLock;
+extern LIST_ENTRY IoNodeHashTable[];
+
+//
+// IoNode Primitives
+//
+
+BOOLEAN
+ReferenceOrCreateIoNode (
+ IN dev_t DeviceSerialNumber,
+ IN ino_t FileSerialNumber,
+ IN BOOLEAN FindOnly,
+ OUT PIONODE *IoNode
+ );
+
+
+BOOLEAN
+LocateIoNode (
+ IN HANDLE FileHandle,
+ OUT PIONODE *IoNode
+ );
+
+
+VOID
+DereferenceIoNode (
+ IN PIONODE IoNode
+ );
+
+//
+// Fd Primitives
+//
+
+PFILEDESCRIPTOR
+AllocateFd(
+ IN PPSX_PROCESS p,
+ IN ULONG Start,
+ OUT PULONG Index
+ );
+
+BOOLEAN
+DeallocateFd(
+ IN PPSX_PROCESS p,
+ IN ULONG Index
+ );
+
+PFILEDESCRIPTOR
+FdIndexToFd(
+ IN PPSX_PROCESS p,
+ IN ULONG Index
+ );
+
+//
+// System Open File Primitives
+//
+
+PSYSTEMOPENFILE
+AllocateSystemOpenFile();
+
+VOID
+DeallocateSystemOpenFile(
+ IN PPSX_PROCESS p,
+ IN PSYSTEMOPENFILE SystemOpenFile
+ );
+
+//
+// System Open File Macros
+//
+
+#define REFERENCE_SYSTEMOPENFILE(systemopenfile) \
+ RtlEnterCriticalSection(&SystemOpenFileLock); \
+ (systemopenfile)->HandleCount++; \
+ RtlLeaveCriticalSection(&SystemOpenFileLock)
+
+VOID
+ForkProcessFileTable(
+ IN PPSX_PROCESS ForkProcess,
+ IN PPSX_PROCESS NewProcess
+ );
+
+VOID
+ExecProcessFileTable(
+ IN PPSX_PROCESS p
+ );
+
+VOID
+CloseProcessFileTable(
+ IN PPSX_PROCESS p
+ );
+
+//
+// Per-Device Type Io structures, types... Filesystems
+// are numbered 'A'..'Z'.
+//
+
+#define PSX_LOCAL_PIPE 1
+#define PSX_CONSOLE_DEV 2
+#define PSX_NULL_DEV 3
+
+//
+// Local Pipes
+//
+
+typedef struct _LOCAL_PIPE {
+ ULONG ReadHandleCount;
+ ULONG WriteHandleCount;
+ LIST_ENTRY WaitingWriters;
+ LIST_ENTRY WaitingReaders;
+ RTL_CRITICAL_SECTION CriticalSection;
+ LONG BufferSize;
+ LONG DataInPipe;
+ PUCHAR WritePointer;
+ PUCHAR ReadPointer;
+ UCHAR Buffer[PIPE_BUF];
+} LOCAL_PIPE, *PLOCAL_PIPE;
+
+extern PSXIO_VECTORS LocalPipeVectors;
+extern PSXIO_VECTORS NamedPipeVectors;
+
+VOID
+InitializeLocalPipe(
+ IN PLOCAL_PIPE Pipe
+ );
+
+//
+// Files
+//
+
+extern PSXIO_VECTORS FileVectors;
+
+//
+// Console
+//
+
+extern PSXIO_VECTORS ConVectors;
+
+//
+// Null device
+//
+
+extern PSXIO_VECTORS NullVectors;
+
+NTSTATUS
+PsxApiHandleConnectionRequest(
+ IN PPSX_API_MSG Message
+ );
+
+BOOLEAN
+PendingSignalHandledInside(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m,
+ IN sigset_t *RestoreBlockSigset OPTIONAL
+ );
+
+NTSTATUS
+PsxApiRequestThread(
+ IN PVOID ThreadParameter
+ );
+
+VOID
+ApiReply(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m,
+ IN sigset_t *RestoreBlockSigset OPTIONAL
+ );
+
+ULONG
+PsxDetermineFileClass(
+ IN HANDLE FileHandle
+ );
+
+ULONG
+PsxStatusToErrno(
+ IN NTSTATUS Status
+ );
+
+ULONG
+PsxStatusToErrnoPath(
+ IN PUNICODE_STRING Path
+ );
+
+//
+// Stuff for file record locking
+//
+
+typedef struct _SYSFLOCK {
+ LIST_ENTRY Links;
+ SHORT Type; // F_RDLCK or F_WRLCK
+ SHORT Whence; // SEEK_SET, etc.
+ off_t Start; // Starting offset
+ off_t Len; // Length of region
+ pid_t Pid; // Pid of lock owner
+} SYSFLOCK, *PSYSFLOCK;
+
+//
+// hack types
+//
+
+typedef struct _SYSTEMMSG {
+ LIST_ENTRY Links;
+ PORT_MESSAGE PortMsg;
+} SYSTEMMSG, *PSYSTEMMSG;
+
+
+typedef
+BOOLEAN
+(* PPSX_API_ROUTINE)(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+//
+// Api Prototypes
+//
+
+BOOLEAN
+PsxFork(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxExec(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxWaitPid(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxExit(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxKill(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxSigAction(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxSigProcMask(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxSigPending(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxSigSuspend(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxAlarm(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxSleep(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxGetIds(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxSetUid(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxSetGid(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxGetGroups(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxGetLogin(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxCUserId(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxSetSid(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxSetPGroupId(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxUname(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxTime(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxGetProcessTimes(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxTtyName(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxIsatty(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxSysconf(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxOpenDir(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxReadDir(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxRewindDir(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxCloseDir(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxChDir(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxGetCwd(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxOpen(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxCreat(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxUmask(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxLink(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxMkDir(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxMkFifo(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxRmDir(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxRename(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxStat(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxFStat(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxAccess(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxChmod(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxChown(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxUtime(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxPathConf(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxFPathConf(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxPipe(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxClose(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxRead(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxWrite(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxFcntl(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxDup(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxDup2(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxLseek(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxTcGetAttr(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxTcSetAttr(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxTcSendBreak(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxTcDrain(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxTcFlush(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxTcFlow(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxTcGetPGrp(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxTcSetPGrp(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxGetPwUid(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxGetPwNam(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxGetGrGid(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxGetGrNam(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxUnlink(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxFtruncate(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxNull(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+#ifdef PSX_SOCKET
+
+BOOLEAN
+PsxSocket(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxAccept(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxBind(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxConnect(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxGetPeerName(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxGetSockName(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxGetSockOpt(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxListen(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxRecv(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxRecvFrom(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxSend(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxSendTo(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxSetSockOpt(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+BOOLEAN
+PsxShutdown(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ );
+
+#endif // PSX_SOCKET
+
+PVOID
+PsxViewSessionData(
+ IN ULONG SessionId,
+ OUT PHANDLE Section
+ );
+
+ULONG
+GetOffsetBySid(
+ PSID Sid
+ );
+
+PSID
+GetSidByOffset(
+ ULONG Offset
+ );
+
+VOID
+MapSidToOffset(
+ PSID Sid,
+ ULONG Offset
+ );
+
+VOID
+InitSidList(
+ VOID
+ );
+
+VOID
+ModeToAccessMask(
+ mode_t Mode,
+ PACCESS_MASK UserAccess,
+ PACCESS_MASK GroupAccess,
+ PACCESS_MASK OtherAccess
+ );
+
+mode_t
+AccessMaskToMode(
+ ACCESS_MASK UserAccess,
+ ACCESS_MASK GroupAccess,
+ ACCESS_MASK OtherAccess
+ );
+
+VOID
+AlarmThreadRoutine(
+ VOID
+ );
+
+HANDLE AlarmThreadHandle;
+
+PSID
+MakeSid(
+ PSID DomainSid,
+ ULONG RelativeId
+ );
+
+VOID
+EndImpersonation(
+ VOID
+ );
+
+uid_t
+MakePosixId(
+ PSID Sid
+ );
+
+NTSTATUS
+RtlInterpretPosixAcl(
+ IN ULONG AclRevision,
+ IN PSID UserSid,
+ IN PSID GroupSid,
+ IN PACL Acl,
+ OUT PACCESS_MASK UserAccess,
+ OUT PACCESS_MASK GroupAccess,
+ OUT PACCESS_MASK OtherAccess
+ );
+
+NTSTATUS
+RtlMakePosixAcl(
+ IN ULONG AclRevision,
+ IN PSID UserSid,
+ IN PSID GroupSid,
+ IN ACCESS_MASK UserAccess,
+ IN ACCESS_MASK GroupAccess,
+ IN ACCESS_MASK OtherAccess,
+ IN ULONG AclLength,
+ OUT PACL Acl,
+ OUT PULONG ReturnLength
+ );
+
+VOID
+ReleaseFlocksByPid(
+ PIONODE IoNode,
+ pid_t pid
+ );
+
+BOOLEAN
+DoFlockStuff(
+ PPSX_PROCESS Proc,
+ PPSX_API_MSG m,
+ int command,
+ IN PFILEDESCRIPTOR Fd,
+ IN OUT struct flock *new,
+ OUT int *error
+ );
+
+#if DBG
+VOID
+DumpFlockList(
+ PIONODE IoNode
+ );
+#endif // DBG
+
+NTSTATUS
+InitConnectingTerminalList(
+ VOID
+ );
+
+NTSTATUS
+AddConnectingTerminal(
+ int Id,
+ HANDLE CommPort,
+ HANDLE ReqPort
+ );
+
+PPSX_CONTROLLING_TTY
+GetConnectingTerminal(
+ int Id
+ );
+
+ULONG
+OpenTty(
+ PPSX_PROCESS p,
+ PFILEDESCRIPTOR Fd,
+ ULONG DesiredAccess,
+ ULONG Flags,
+ BOOLEAN NewOpen
+ );
+
+ULONG
+OpenDevNull(
+ PPSX_PROCESS p,
+ PFILEDESCRIPTOR Fd,
+ ULONG DesiredAccess,
+ ULONG Flags
+ );
+
+dev_t
+GetFileDeviceNumber(
+ PUNICODE_STRING Path
+ );
+
+NTSTATUS
+InitSecurityDescriptor(
+ IN OUT PSECURITY_DESCRIPTOR pSD,
+ IN PUNICODE_STRING pFileName,
+ IN HANDLE Process,
+ IN mode_t Mode,
+ OUT PVOID *pvSaveMem
+ );
+
+VOID
+DeInitSecurityDescriptor(
+ IN PSECURITY_DESCRIPTOR pSD,
+ IN PVOID *pvSaveMem
+ );
+
+BOOLEAN
+IsGroupOrphaned(
+ IN pid_t ProcessGroup
+ );
+
+NTSTATUS
+ExecForeignImage(
+ PPSX_PROCESS p,
+ PPSX_API_MSG m,
+ PUNICODE_STRING Image,
+ PUNICODE_STRING CurDir
+ );
+
+
+#endif // _PSXP_
diff --git a/private/posix/inc/sesport.h b/private/posix/inc/sesport.h
new file mode 100644
index 000000000..efe33edd7
--- /dev/null
+++ b/private/posix/inc/sesport.h
@@ -0,0 +1,270 @@
+
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sesport.h
+
+Abstract:
+
+Author:
+
+ Ellen Aycock-Wright (ellena) Sept-15-1991
+
+Revision History:
+
+--*/
+
+#ifndef _SESPORT_
+
+#define _SESPORT_
+
+#include <posix/sys/types.h>
+#include <posix/termios.h>
+
+#define PSX_SS_SESSION_PORT_NAME L"\\PSXSS\\SESPORT"
+#define PSX_SES_BASE_PORT_NAME "\\PSXSS\\PSXSES"
+#define PSX_SES_BASE_PORT_NAME_LENGTH 32
+#define PSX_SES_BASE_PORT_PREFIX 'P'
+#define PSX_SES_BASE_DATA_PREFIX 'D'
+
+#define PSX_CON_PORT_DATA_SIZE 0x1000L
+
+#define CONSTRUCT_PSX_SES_NAME(Name, t, id) \
+ { char *p; \
+ \
+ strcpy(Name, PSX_SES_BASE_PORT_NAME); \
+ p = &Name[sizeof(PSX_SES_BASE_PORT_NAME)-1]; \
+ *(p++) = '\\'; \
+ \
+ *(p++) = t; \
+ \
+ RtlIntegerToChar(id, 10, 16, p); \
+ }
+
+#define PSX_GET_SESSION_PORT_NAME(SessionName, Id) \
+ sprintf(SessionName, "%s\\%c%d", PSX_SES_BASE_PORT_NAME, \
+ PSX_SES_BASE_PORT_PREFIX, \
+ Id);
+
+#define PSX_GET_SESSION_DATA_NAME(SessionName, Id) \
+ sprintf(SessionName, "%s\\%c%d", PSX_SES_BASE_PORT_NAME, \
+ PSX_SES_BASE_DATA_PREFIX, \
+ Id);
+
+/*
+ * Session Console ConnectInfo struct
+ */
+
+typedef struct _SCCONNECTINFO {
+ int dummy;
+} SCCONNECTINFO, *PSCCONNECTINFO;
+
+/*
+ * Console requests
+ * these are mapped 1-1 to the win32 Console services
+ */
+
+typedef enum {
+ ScCreateFile,
+ ScOpenFile,
+ ScCloseFile,
+ ScReadFile,
+ ScWriteFile,
+ ScKbdCharIn,
+ ScIsatty,
+ ScIsatty2
+} SCCONREQUESTNUMBER;
+
+
+typedef struct _CONFILEREQUEST {
+ HANDLE Handle;
+ LONG Len;
+ ULONG Flags;
+ } CONFILEREQUEST;
+
+//
+// bits for CONFILEREQUEST.Flags
+//
+
+#define PSXSES_NONBLOCK 0x1
+
+typedef struct _SCCONREQUEST {
+ SCCONREQUESTNUMBER Request;
+ union {
+ CONFILEREQUEST IoBuf;
+ CHAR AsciiChar;
+ } d;
+} SCCONREQUEST, *PSCCONREQUEST;
+
+
+/* -------- End of Console Requests section -------- */
+
+/*
+ * TaskManager requests
+ */
+
+typedef enum {
+ TmExit,
+ TmTitle
+} SCTMREQUESTNUMBER;
+
+
+typedef struct {
+ SCTMREQUESTNUMBER Request;
+ ULONG ExitStatus; // for TmExit
+} SCTMREQUEST, *PSCTMREQUEST;
+
+
+/* -------- End of TaskManager Requests section -------- */
+
+/*
+ * Termios control requests
+ */
+
+typedef enum {
+ TcGetAttr,
+ TcSetAttr,
+ TcDrain,
+ TcFlow,
+ TcFlush,
+ TcGetPGrp,
+ TcSetPGrp,
+ TcSendBreak
+} SCTCREQUESTNUMBER;
+
+typedef struct _SCTCREQUEST {
+ SCTCREQUESTNUMBER Request;
+ LONG FileDes;
+ struct termios Termios;
+ LONG OptionalActions;
+} SCTCREQUEST, *PSCTCREQUEST;
+
+/*
+ * PsxSes requests:
+ * Request for CONSOLE services from PSX SS and PSX clients to the console
+ * process
+ */
+
+typedef enum _SCREQUESTNUMBER {
+ ConRequest,
+ TaskManRequest,
+ TcRequest
+
+} SCREQUESTNUMBER;
+
+
+#ifdef NTPSX_ONLY
+typedef struct _SCREQUESTMSG {
+ PORT_MESSAGE h;
+ union {
+ SCCONNECTINFO ConnectionRequest;
+ struct {
+ SCREQUESTNUMBER Request;
+ NTSTATUS Status; // returned status for the request.
+ union {
+ SCCONREQUEST Con;
+ SCTMREQUEST Tm;
+ SCTCREQUEST Tc;
+ } d;
+ };
+ };
+} SCREQUESTMSG, *PSCREQUESTMSG;
+
+
+/*
+ * PSX SS Session ConnectInfo struct
+ */
+
+typedef union _PSXSESCONNECTINFO {
+ struct {
+ int SessionUniqueId;
+ } In;
+
+ struct {
+ HANDLE SessionPortHandle;
+ } Out;
+
+} PSXSESCONNECTINFO, *PPSXSESCONNECTINFO;
+
+
+
+/*
+ * Psx SS Session requests
+ * Requests from the session console process (PSXSES.EXE) to the PSX SS
+ * e.g. Create session, CtrlBreak, etc.
+ */
+
+typedef enum _PSXSESREQUESTNUMBER {
+ SesConCreate,
+ SesConSignal
+
+} PSXSESREQUESTNUMBER;
+
+#endif // NTPSX_ONLY
+
+typedef struct {
+ ULONG SessionUniqueId;
+ HANDLE SessionPort;
+ HANDLE ConsoleProcessHandle;
+ int OpenFiles;
+ int PgmNameOffset;
+ int CurrentDirOffset;
+ int ArgsOffset;
+ int EnvironmentOffset;
+ PVOID Buffer; // for io and foreign process args
+} SCREQ_CREATE, *PSCREQ_CREATE;
+
+
+typedef struct {
+ int Type;
+} SCREQ_SIGNAL;
+
+//
+// Values for SCREQ_SIGNAL.Type
+//
+
+#define PSX_SIGINT 0
+#define PSX_SIGQUIT 1
+#define PSX_SIGKILL 2
+#define PSX_SIGTSTP 3
+
+typedef struct {
+ ULONG Status;
+ HANDLE Session;
+} SCREQ_REPLY, *PSCREQ_REPLY;
+
+#ifdef NTPSX_ONLY
+
+typedef struct _PSXSESREQUESTMSG {
+ PORT_MESSAGE h;
+ union {
+ PSXSESCONNECTINFO ConnectionRequest;
+ struct {
+ HANDLE Session;
+ PSXSESREQUESTNUMBER Request;
+ NTSTATUS Status;
+ ULONG UniqueId;
+ union {
+ SCREQ_CREATE Create;
+ SCREQ_SIGNAL Signal;
+ // SCREQ_REPLY Reply;
+ } d;
+ };
+ };
+} PSXSESREQUESTMSG, *PPSXSESREQUESTMSG;
+
+
+/*
+ * Common macros to access PORT_MESSAGE fields
+ */
+#define PORT_MSG_TYPE(m) ((m).h.u2.s2.Type)
+#define PORT_MSG_DATA_LENGTH(m) ((m).h.u1.s1.DataLength)
+#define PORT_MSG_TOTAL_LENGTH(m) ((m).h.u1.s1.TotalLength)
+#define PORT_MSG_ZERO_INIT(m) ((m).h.u2.ZeroInit)
+
+#endif // NTPSX_ONLY
+
+#endif
diff --git a/private/posix/pcd.doc b/private/posix/pcd.doc
new file mode 100644
index 000000000..5cdc78ba7
--- /dev/null
+++ b/private/posix/pcd.doc
Binary files differ
diff --git a/private/posix/programs/bsdlib/bcopy.c b/private/posix/programs/bsdlib/bcopy.c
new file mode 100644
index 000000000..b1611c72c
--- /dev/null
+++ b/private/posix/programs/bsdlib/bcopy.c
@@ -0,0 +1,9 @@
+#include <string.h>
+/*
+ * Bcopy: Posix implementation MSS
+ */
+
+void bcopy(const void *src, void *dst, size_t len)
+{
+ memcpy(dst, src, len);
+}
diff --git a/private/posix/programs/bsdlib/bzero.c b/private/posix/programs/bsdlib/bzero.c
new file mode 100644
index 000000000..71e5b48a2
--- /dev/null
+++ b/private/posix/programs/bsdlib/bzero.c
@@ -0,0 +1,10 @@
+#include <string.h>
+
+/*
+ * bzero -- Posix implementation usint memset DF_MSS
+ */
+void bzero(void *b, register size_t length)
+{
+ memset(b, 0, length);
+
+}
diff --git a/private/posix/programs/bsdlib/for2bak.c b/private/posix/programs/bsdlib/for2bak.c
new file mode 100644
index 000000000..0fb7357ac
--- /dev/null
+++ b/private/posix/programs/bsdlib/for2bak.c
@@ -0,0 +1,13 @@
+#include <stdio.h>
+/*
+ * For2bak: Converts a string's fore slashes to back slashes. MSS
+ */
+
+void for2bak(char * str)
+{
+ while (*str) {
+ if(*str == '/')
+ *str = '\\';
+ str++;
+ }
+}
diff --git a/private/posix/programs/bsdlib/fts.c b/private/posix/programs/bsdlib/fts.c
new file mode 100644
index 000000000..b5e6a1d5f
--- /dev/null
+++ b/private/posix/programs/bsdlib/fts.c
@@ -0,0 +1,870 @@
+ /* Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#ifdef DF_POSIX
+#include <misc.h>
+#endif
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)fts.c 5.19 (Berkeley) 5/9/91";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/cdefs.h>
+#ifdef _POSIX_SOURCE //DF_DSC POSIX does not need this
+#else // only MAXPATHLEN was found there
+ #include <sys/param.h> // and it wants machine directory stuff
+#endif
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#include "fts.h"
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+extern void bzero();
+extern void bcopy();
+extern char *rindex();
+extern int lstat();
+
+static FTSENT *fts_alloc(), *fts_build(), *fts_sort();
+static void fts_load(), fts_lfree();
+static u_short fts_stat();
+static char *fts_path();
+
+#define ISSET(opt) (sp->fts_options & opt)
+#define SET(opt) (sp->fts_options |= opt)
+
+#define CHDIR(sp, path) (!ISSET(FTS_NOCHDIR) && chdir(path))
+
+#ifdef _POSIX_SOURCE //DF_DSC fchdir not in POSIX
+#else
+#define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd))
+#endif
+
+/* fts_build flags */
+#define BCHILD 1 /* from fts_children */
+#define BREAD 2 /* from fts_read */
+
+FTS *
+fts_open(argv, options, compar)
+ char * const *argv;
+ register int options;
+ int (*compar)(const FTSENT *, const FTSENT *);
+{
+ register FTS *sp;
+ register FTSENT *p, *root;
+ register int nitems, maxlen;
+ FTSENT *parent, *tmp;
+ int len;
+
+ /* Allocate/initialize the stream */
+ if (!(sp = (FTS *)malloc((u_int)sizeof(FTS))))
+ return(NULL);
+ bzero(sp, sizeof(FTS));
+ sp->fts_compar = compar;
+ sp->fts_options = options;
+
+ /* Logical walks turn on NOCHDIR; symbolic links are too hard. */
+ if (ISSET(FTS_LOGICAL))
+ SET(FTS_NOCHDIR);
+
+// force NOCHDIR because chdir("..") fails for large trees //DF_DSC
+ SET(FTS_NOCHDIR);
+
+
+ /* Allocate/initialize root's parent. */
+ if (!(parent = fts_alloc(sp, "", 0)))
+ goto mem1;
+ parent->fts_level = FTS_ROOTPARENTLEVEL;
+
+ /* Allocate/initialize root(s). */
+ maxlen = -1;
+ for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) {
+ if (!(len = strlen(*argv))) {
+ errno = ENOENT;
+ goto mem2;
+ }
+ if (maxlen < len)
+ maxlen = len;
+ p = fts_alloc(sp, *argv, len);
+ p->fts_level = FTS_ROOTLEVEL;
+ p->fts_parent = parent;
+ /*
+ * If comparison routine supplied, traverse in sorted
+ * order; otherwise traverse in the order specified.
+ */
+ if (compar) {
+ p->fts_link = root;
+ root = p;
+ p->fts_accpath = p->fts_name;
+ if (!(options & FTS_NOSTAT))
+ p->fts_info = fts_stat(sp, p, 0);
+ } else {
+ p->fts_link = NULL;
+ if (!root)
+ tmp = root = p;
+ else {
+ tmp->fts_link = p;
+ tmp = p;
+ }
+ }
+ }
+ if (compar && nitems > 1)
+ root = fts_sort(sp, root, nitems);
+
+ /*
+ * Allocate a dummy pointer and make fts_read think that we've just
+ * finished the node before the root(s); set p->fts_info to FTS_NS
+ * so that everything about the "current" node is ignored.
+ */
+ if (!(sp->fts_cur = fts_alloc(sp, "", 0)))
+ goto mem2;
+ sp->fts_cur->fts_link = root;
+ sp->fts_cur->fts_info = FTS_NS;
+
+ /* Start out with at least 1K+ of path space. */
+#ifdef _POSIX_SOURCE //DF_POSIX POSIX defines PATH_MAX
+ if (!fts_path(sp, __max(maxlen, PATH_MAX)))
+ goto mem3;
+ if ((sp->fts_rpath = malloc(PATH_MAX)) == NULL)
+ goto mem4;
+#else
+ if (!fts_path(sp, MAX(maxlen, MAXPATHLEN)))
+ goto mem3;
+#endif
+
+ /*
+ * If using chdir(2), grab a file descriptor pointing to dot to insure
+ * that we can get back here; this could be avoided for some paths,
+ * but almost certainly not worth the effort. Slashes, symbolic links,
+ * and ".." are all fairly nasty problems. Note, if we can't get the
+ * descriptor we run anyway, just more slowly.
+ */
+#ifdef _POSIX_SOURCE //DF_DSC use getpwd because no fchdir for later
+ if (!ISSET(FTS_NOCHDIR) && (getcwd(sp->fts_rpath, PATH_MAX)) == NULL)
+#else
+ if (!ISSET(FTS_NOCHDIR) && (sp->fts_rfd = open(".", O_RDONLY, 0)) < 0)
+#endif
+ SET(FTS_NOCHDIR);
+
+ return(sp);
+#ifdef _POSIX_SOURCE //DF_DSC
+mem4: free(sp->fts_path);
+#endif
+mem3: free(sp->fts_cur);
+mem2: fts_lfree(root);
+ free(parent);
+mem1: free(sp);
+ return(NULL);
+}
+
+static void
+fts_load(sp, p)
+ FTS *sp;
+ register FTSENT *p;
+{
+ register int len;
+ register char *cp;
+
+ /*
+ * Load the stream structure for the next traversal. Since we don't
+ * actually enter the directory until after the preorder visit, set
+ * the fts_accpath field specially so the chdir gets done to the right
+ * place and the user can access the first node.
+ */
+ len = p->fts_pathlen = p->fts_namelen;
+ bcopy(p->fts_name, sp->fts_path, len + 1);
+ if ((cp = rindex(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {
+ len = strlen(++cp);
+ bcopy(cp, p->fts_name, len + 1);
+ p->fts_namelen = len;
+ }
+ p->fts_accpath = p->fts_path = sp->fts_path;
+
+ p->fts_info = fts_stat(sp, p, 0);
+ sp->rdev = p->fts_statb.st_dev;
+}
+
+fts_close(sp)
+ FTS *sp;
+{
+ register FTSENT *freep, *p;
+ int saved_errno;
+
+ if (sp->fts_cur) {
+ /*
+ * This still works if we haven't read anything -- the dummy
+ * structure points to the root list, so we step through to
+ * the end of the root list which has a valid parent pointer.
+ */
+ for (p = sp->fts_cur; p->fts_level > FTS_ROOTPARENTLEVEL;) {
+ freep = p;
+ p = p->fts_link ? p->fts_link : p->fts_parent;
+ free(freep);
+ }
+ free(p);
+ }
+
+ /* Free up child linked list, sort array, path buffer. */
+ if (sp->fts_child)
+ fts_lfree(sp->fts_child);
+ if (sp->fts_array)
+ free(sp->fts_array);
+ free(sp->fts_path);
+
+ /* Return to original directory, save errno if necessary. */
+ if (!ISSET(FTS_NOCHDIR)) {
+#ifdef _POSIX_SOURCE
+ saved_errno = chdir(sp->fts_rpath) ? errno : 0;
+ free(sp->fts_rpath);
+#else
+ saved_errno = fchdir(sp->fts_rfd) ? errno : 0;
+ (void)close(sp->fts_rfd);
+#endif
+ }
+
+ /* Free up the stream pointer. */
+ free(sp);
+
+ /* Set errno and return. */
+ if (!ISSET(FTS_NOCHDIR) && saved_errno) {
+ errno = saved_errno;
+ return(-1);
+ }
+ return(0);
+}
+
+/*
+ * Special case a root of "/" so that slashes aren't appended causing
+ * paths to be written as "//foo".
+ */
+#define NAPPEND(p) \
+ (p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 && \
+ p->fts_path[0] == '/' ? 0 : p->fts_pathlen)
+
+FTSENT *
+fts_read(sp)
+ register FTS *sp;
+{
+ register FTSENT *p, *tmp;
+ register int instr;
+ register char *t;
+
+ /* If finished or unrecoverable error, return NULL. */
+ if (!sp->fts_cur || ISSET(FTS_STOP))
+ return(NULL);
+
+ /* Set current node pointer. */
+ p = sp->fts_cur;
+
+ /* Save and zero out user instructions. */
+ instr = p->fts_instr;
+ p->fts_instr = FTS_NOINSTR;
+
+ /* If used fts_link pointer for cycle detection, restore it. */
+ if (sp->fts_savelink) {
+ p->fts_link = sp->fts_savelink;
+ sp->fts_savelink = NULL;
+ }
+
+ /* Any type of file may be re-visited; re-stat and re-turn. */
+ if (instr == FTS_AGAIN) {
+ p->fts_info = fts_stat(sp, p, 0);
+ return(p);
+ }
+
+ /*
+ * Following a symlink -- SLNONE test allows application to see
+ * SLNONE and recover.
+ */
+ if (instr == FTS_FOLLOW &&
+ (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
+ p->fts_info = fts_stat(sp, p, 1);
+ return(p);
+ }
+
+ /* Directory in pre-order. */
+ if (p->fts_info == FTS_D) {
+ /* If skipped or crossed mount point, do post-order visit. */
+ if (instr == FTS_SKIP ||
+ ISSET(FTS_XDEV) && p->fts_statb.st_dev != sp->rdev) {
+ if (sp->fts_child) {
+ fts_lfree(sp->fts_child);
+ sp->fts_child = NULL;
+ }
+ p->fts_info = FTS_DP;
+ return(p);
+ }
+
+ /*
+ * Cd to the subdirectory, reading it if haven't already. If
+ * the read fails for any reason, or the directory is empty,
+ * the fts_info field of the current node is set by fts_build.
+ * If have already read and now fail to chdir, whack the list
+ * to make the names come out right, and set the parent state
+ * so the application will eventually get an error condition.
+ * If haven't read and fail to chdir, check to see if we're
+ * at the root node -- if so, we have to get back or the root
+ * node may be inaccessible.
+ */
+ if (sp->fts_child) {
+ if (CHDIR(sp, p->fts_accpath)) {
+ p->fts_parent->fts_cderr = errno;
+ for (p = sp->fts_child; p; p = p->fts_link)
+ p->fts_accpath =
+ p->fts_parent->fts_accpath;
+ }
+ } else if (!(sp->fts_child = fts_build(sp, BREAD))) {
+ if ISSET(FTS_STOP)
+ return(NULL);
+#ifdef _POSIX_SOURCE
+ if (p->fts_level == FTS_ROOTLEVEL &&
+ CHDIR (sp, sp->fts_rpath)) {
+ SET(FTS_STOP);
+ return(NULL);
+ }
+#else
+ if (p->fts_level == FTS_ROOTLEVEL &&
+ FCHDIR(sp, sp->fts_rfd)) {
+ SET(FTS_STOP);
+ return(NULL);
+ }
+#endif
+ return(p);
+ }
+ p = sp->fts_child;
+ sp->fts_child = NULL;
+ goto name;
+ }
+
+ /* Move to next node on this level. */
+next: tmp = p;
+ if (p = p->fts_link) {
+ free(tmp);
+
+ /* If reached the top, load the paths for the next root. */
+ if (p->fts_level == FTS_ROOTLEVEL) {
+ fts_load(sp, p);
+ return(sp->fts_cur = p);
+ }
+
+ /* User may have called fts_set on the node. */
+ if (p->fts_instr == FTS_SKIP)
+ goto next;
+ if (p->fts_instr == FTS_FOLLOW) {
+ p->fts_info = fts_stat(sp, p, 1);
+ p->fts_instr = FTS_NOINSTR;
+ }
+
+name: t = sp->fts_path + NAPPEND(p->fts_parent);
+ *t++ = '/';
+ bcopy(p->fts_name, t, p->fts_namelen + 1);
+ return(sp->fts_cur = p);
+ }
+
+ /* Move up to the parent node. */
+ p = tmp->fts_parent;
+ free(tmp);
+
+ if (p->fts_level == FTS_ROOTPARENTLEVEL) {
+ /*
+ * Done; free everything up and set errno to 0 so the user
+ * can distinguish between error and EOF.
+ */
+ free(p);
+ errno = 0;
+ return(sp->fts_cur = NULL);
+ }
+
+ sp->fts_path[p->fts_pathlen] = '\0';
+
+ /*
+ * Cd back up to the parent directory. If at a root node, have to cd
+ * back to the original place, otherwise may not be able to access the
+ * original node on post-order.
+ */
+ if (p->fts_level == FTS_ROOTLEVEL) {
+#ifdef _POSIX_SOURCE
+ if (CHDIR (sp, sp->fts_rpath)) {
+#else
+ if (FCHDIR(sp, sp->fts_rfd)) {
+#endif
+ SET(FTS_STOP);
+ return(NULL);
+ }
+ }
+ else if (CHDIR(sp, "..")) {
+ SET(FTS_STOP);
+ return(NULL);
+ }
+
+ /*
+ * If had a chdir error when trying to get into the directory, set the
+ * info field to reflect this, and restore errno. The error indicator
+ * has to be reset to 0 so that if the user does an FTS_AGAIN, it all
+ * works.
+ */
+ if (p->fts_cderr) {
+ errno = p->fts_cderr;
+ p->fts_cderr = 0;
+ p->fts_info = FTS_ERR;
+ } else
+ p->fts_info = FTS_DP;
+ return(sp->fts_cur = p);
+}
+
+/*
+ * Fts_set takes the stream as an argument although it's not used in this
+ * implementation; it would be necessary if anyone wanted to add global
+ * semantics to fts using fts_set. An error return is allowed for similar
+ * reasons.
+ */
+/* ARGSUSED */
+fts_set(sp, p, instr)
+ FTS *sp;
+ FTSENT *p;
+ int instr;
+{
+ p->fts_instr = instr;
+ return(0);
+}
+
+FTSENT *
+fts_children(sp)
+ register FTS *sp;
+{
+ register FTSENT *p;
+ int fd;
+#ifdef _POSIX_SOURCE //DF_DSC
+ char this_path [PATH_MAX];
+#endif
+
+ /* Set current node pointer. */
+ p = sp->fts_cur;
+
+ /*
+ * Set errno to 0 so that user can tell the difference between an
+ * error and a directory without entries. If not a directory being
+ * visited in *pre-order*, or we've already had fatal errors, return
+ * immediately.
+ */
+ errno = 0;
+ if (ISSET(FTS_STOP) || p->fts_info != FTS_D && p->fts_info != FTS_DNR)
+ return(NULL);
+
+ /* Free up any previous child list. */
+ if (sp->fts_child)
+ fts_lfree(sp->fts_child);
+
+ /*
+ * If using chdir on a relative path and called BEFORE fts_read does
+ * its chdir to the root of a traversal, we can lose -- we need to
+ * chdir into the subdirectory, and we don't know where the current
+ * directory is, so we can't get back so that the upcoming chdir by
+ * fts_read will work.
+ */
+ if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||
+ ISSET(FTS_NOCHDIR))
+ return(sp->fts_child = fts_build(sp, BCHILD));
+#ifdef _POSIX_SOURCE //DF_DSC Because no fchdir
+ if ((getcwd(this_path, PATH_MAX)) == NULL)
+#else
+ if ((fd = open(".", O_RDONLY, 0)) < 0)
+#endif
+ return(NULL);
+ sp->fts_child = fts_build(sp, BCHILD);
+#ifdef _POSIX_SOURCE
+ if (chdir(this_path))
+ return(NULL);
+#else
+ if (fchdir(fd))
+ return(NULL);
+ (void)close(fd);
+#endif
+ return(sp->fts_child);
+}
+
+/*
+ * This is the tricky part -- do not casually change *anything* in here. The
+ * idea is to build the linked list of entries that are used by fts_children
+ * and fts_read. There are lots of special cases.
+ *
+ * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is
+ * set and it's a physical walk (so that symbolic links can't be directories),
+ * we assume that the number of subdirectories in a node is equal to the number
+ * of links to the parent. This allows stat calls to be skipped in any leaf
+ * directories and for any nodes after the directories in the parent node have
+ * been found. This empirically cuts the stat calls by about 2/3.
+ */
+#define ISDOT(a) (a[0] == '.' && (!a[1] || a[1] == '.' && !a[2]))
+
+static FTSENT *
+fts_build(sp, type)
+ register FTS *sp;
+ int type;
+{
+ register struct dirent *dp;
+ register FTSENT *p, *head;
+ register int nitems;
+ FTSENT *cur;
+ DIR *dirp;
+ int cderr, descend, len, level, maxlen, nlinks, saved_errno;
+ char *cp;
+
+ /* Set current node pointer. */
+ cur = sp->fts_cur;
+
+ /*
+ * Open the directory for reading. If this fails, we're done.
+ * If being called from fts_read, set the fts_info field.
+ */
+ if (!(dirp = opendir(cur->fts_accpath))) {
+ if (type == BREAD)
+ cur->fts_info = FTS_DNR;
+ return(NULL);
+ }
+
+ /*
+ * Nlinks is the number of possible entries of type directory in the
+ * directory if we're cheating on stat calls, 0 if we're not doing
+ * any stat calls at all, -1 if we're doing stats on everything.
+ */
+ nlinks =
+ ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL) ?
+ cur->fts_statb.st_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2) : -1;
+
+ /*
+ * If we're going to need to stat anything or we want to descend
+ * and stay in the directory, chdir. If this fails we keep going.
+ * We won't be able to stat anything, but we can still return the
+ * names themselves. Note, that since fts_read won't be able to
+ * chdir into the directory, it will have to return different path
+ * names than before, i.e. "a/b" instead of "b". Since the node
+ * has already been visited in pre-order, have to wait until the
+ * post-order visit to return the error. This is all fairly nasty.
+ * If a program needed sorted entries or stat information, they had
+ * better be checking FTS_NS on the returned nodes.
+ */
+ if (nlinks || type == BREAD)
+#ifdef _POSIX_SOURCE
+ if (CHDIR(sp, cur->fts_accpath)) {
+#else
+ if (FCHDIR(sp, dirfd(dirp))) {
+#endif
+ if (type == BREAD)
+ cur->fts_cderr = errno;
+ descend = nlinks = 0;
+ cderr = 1;
+ } else {
+ descend = 1;
+ cderr = 0;
+ }
+ else
+ descend = 0;
+
+ /*
+ * Figure out the max file name length that can be stored in the
+ * current path -- the inner loop allocates more path as necessary.
+ * We really wouldn't have to do the maxlen calculations here, we
+ * could do them in fts_read before returning the path, but it's a
+ * lot easier here since the length is part of the dirent structure.
+ *
+ * If not changing directories set a pointer so that we can just
+ * append each new name into the path.
+ */
+ maxlen = sp->fts_pathlen - cur->fts_pathlen - 1;
+ len = NAPPEND(cur);
+ if (ISSET(FTS_NOCHDIR)) {
+ cp = sp->fts_path + len;
+ *cp++ = '/';
+ }
+
+ level = cur->fts_level + 1;
+
+ /* Read the directory, attaching each entry to the `link' pointer. */
+ for (head = NULL, nitems = 0; dp = readdir(dirp);) {
+ if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
+ continue;
+
+#ifdef _POSIX_SOURCE
+ if (!(p = fts_alloc(sp, dp->d_name, strlen (dp->d_name))))
+#else
+ if (!(p = fts_alloc(sp, dp->d_name, (int)dp->d_namlen)))
+#endif
+ goto mem1;
+#ifdef _POSIX_SOURCE
+ if (strlen (dp->d_name) > (unsigned)maxlen) {
+#else
+ if (dp->d_namlen > maxlen) {
+#endif
+#ifdef _POSIX_SOURCE
+ if (!fts_path(sp, strlen (dp->d_name))) {
+#else
+ if (!fts_path(sp, (int)dp->d_namlen)) {
+#endif
+ /*
+ * No more memory for path or structures. Save
+ * errno, free up the current structure and the
+ * structures already allocated.
+ */
+mem1: saved_errno = errno;
+ if (p)
+ free(p);
+ fts_lfree(head);
+ (void)closedir(dirp);
+ errno = saved_errno;
+ cur->fts_info = FTS_ERR;
+ SET(FTS_STOP);
+ return(NULL);
+ }
+ maxlen = sp->fts_pathlen - sp->fts_cur->fts_pathlen - 1;
+ }
+
+#ifdef _POSIX_SOURCE
+ p->fts_pathlen = len + strlen (dp->d_name) + 1;
+#else
+ p->fts_pathlen = len + dp->d_namlen + 1;
+#endif
+ p->fts_parent = sp->fts_cur;
+ p->fts_level = level;
+
+ if (nlinks) {
+ /* Build a file name for fts_stat to stat. */
+ if (ISSET(FTS_NOCHDIR)) {
+ p->fts_accpath = p->fts_path;
+ bcopy(p->fts_name, cp, p->fts_namelen + 1);
+ } else
+ p->fts_accpath = p->fts_name;
+ p->fts_info = fts_stat(sp, p, 0);
+ if (nlinks > 0 && p->fts_info == FTS_D)
+ --nlinks;
+ } else if (cderr) {
+ p->fts_info = ISSET(FTS_NOSTAT) ? FTS_NSOK : FTS_NS;
+ p->fts_accpath = cur->fts_accpath;
+ } else {
+ p->fts_accpath =
+ ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name;
+ p->fts_info = FTS_NSOK;
+ }
+
+ p->fts_link = head;
+ head = p;
+ ++nitems;
+ }
+ (void)closedir(dirp);
+
+ /*
+ * If not changing directories, reset the path back to original
+ * state.
+ */
+ if (ISSET(FTS_NOCHDIR)) {
+ if (cp - 1 > sp->fts_path)
+ --cp;
+ *cp = '\0';
+ }
+
+ /*
+ * If descended after called from fts_children or called from
+ * fts_read and didn't find anything, get back. If can't get
+ * back, we're done.
+ */
+ if (descend && (!nitems || type == BCHILD) && CHDIR(sp, "..")) {
+ cur->fts_info = FTS_ERR;
+ SET(FTS_STOP);
+ return(NULL);
+ }
+
+ /* If we didn't find anything, just do the post-order visit */
+ if (!nitems) {
+ if (type == BREAD)
+ cur->fts_info = FTS_DP;
+ return(NULL);
+ }
+
+ /* Sort the entries. */
+ if (sp->fts_compar && nitems > 1)
+ head = fts_sort(sp, head, nitems);
+ return(head);
+}
+
+static u_short
+fts_stat(sp, p, follow)
+ FTS *sp;
+ register FTSENT *p;
+ int follow;
+{
+ int saved_errno;
+
+ /*
+ * If doing a logical walk, or application requested FTS_FOLLOW, do
+ * a stat(2). If that fails, check for a non-existent symlink. If
+ * fail, return the errno from the stat call.
+ */
+ if (ISSET(FTS_LOGICAL) || follow) {
+ if (stat(p->fts_accpath, &p->fts_statb)) {
+ saved_errno = errno;
+ if (!lstat(p->fts_accpath, &p->fts_statb)) {
+ errno = 0;
+ return(FTS_SLNONE);
+ }
+ errno = saved_errno;
+ bzero(&p->fts_statb, sizeof(struct stat));
+ return(FTS_NS);
+ }
+ } else if (lstat(p->fts_accpath, &p->fts_statb)) {
+ bzero(&p->fts_statb, sizeof(struct stat));
+ return(FTS_NS);
+ }
+
+ /*
+ * Cycle detection is done as soon as we find a directory. Detection
+ * is by brute force; if the tree gets deep enough or the number of
+ * symbolic links to directories high enough something faster might
+ * be worthwhile.
+ */
+ if (S_ISDIR(p->fts_statb.st_mode)) {
+ register FTSENT *t;
+ register dev_t dev;
+ register ino_t ino;
+
+ dev = p->fts_statb.st_dev;
+ ino = p->fts_statb.st_ino;
+ for (t = p->fts_parent; t->fts_level > FTS_ROOTLEVEL;
+ t = t->fts_parent)
+ if (ino == t->fts_statb.st_ino &&
+ dev == t->fts_statb.st_dev) {
+ sp->fts_savelink = p->fts_link;
+ p->fts_link = t;
+ return(FTS_DC);
+ }
+ return(FTS_D);
+ }
+
+#ifndef _POSIX_SOURCE
+ if (S_ISLNK(p->fts_statb.st_mode))
+ return(FTS_SL);
+#endif
+ if (S_ISREG(p->fts_statb.st_mode))
+ return(FTS_F);
+ return(FTS_DEFAULT);
+}
+
+#define R(type, nelem, ptr) \
+ (type *)realloc((void *)ptr, (u_int)((nelem) * sizeof(type)))
+
+static FTSENT *
+fts_sort(sp, head, nitems)
+ FTS *sp;
+ FTSENT *head;
+ register int nitems;
+{
+ register FTSENT **ap, *p;
+
+ /*
+ * Construct an array of pointers to the structures and call qsort(3).
+ * Reassemble the array in the order returned by qsort. If unable to
+ * sort for memory reasons, return the directory entries in their
+ * current order. Allocate enough space for the current needs plus
+ * 40 so we don't realloc one entry at a time.
+ */
+ if (nitems > sp->fts_nitems) {
+ sp->fts_nitems = nitems + 40;
+ if (!(sp->fts_array =
+ R(FTSENT *, sp->fts_nitems, sp->fts_array))) {
+ sp->fts_nitems = 0;
+ return(head);
+ }
+ }
+ for (ap = sp->fts_array, p = head; p; p = p->fts_link)
+ *ap++ = p;
+ qsort(sp->fts_array, nitems, sizeof(FTSENT *), (int (*)(const void *, const void *))sp->fts_compar);
+ for (head = *(ap = sp->fts_array); --nitems; ++ap)
+ ap[0]->fts_link = ap[1];
+ ap[0]->fts_link = NULL;
+ return(head);
+}
+
+static FTSENT *
+fts_alloc(sp, name, len)
+ FTS *sp;
+ char *name;
+ register int len;
+{
+ register FTSENT *p;
+
+ /*
+ * Variable sized structures; the name is the last element so
+ * we allocate enough extra space after the structure to store
+ * it.
+ */
+ if (!(p = (FTSENT *)malloc((size_t)(sizeof(FTSENT) + len))))
+ return(NULL);
+ bcopy(name, p->fts_name, len + 1);
+ p->fts_namelen = len;
+ p->fts_path = sp->fts_path;
+ p->fts_instr = FTS_NOINSTR;
+ p->fts_cderr = 0;
+ p->fts_number = 0;
+ p->fts_pointer = NULL;
+ return(p);
+}
+
+static void
+fts_lfree(head)
+ register FTSENT *head;
+{
+ register FTSENT *p;
+
+ /* Free a linked list of structures. */
+ while (p = head) {
+ head = head->fts_link;
+ free(p);
+ }
+}
+
+/*
+ * Allow essentially unlimited paths; certain programs (find, rm, ls) need to
+ * work on any tree. Most systems will allow creation of paths much longer
+ * than MAXPATHLEN, even though the kernel won't resolve them. Add an extra
+ * 128 bytes to the requested size so that we don't realloc the path 2 bytes
+ * at a time.
+ */
+static char *
+fts_path(sp, size)
+ FTS *sp;
+ int size;
+{
+ sp->fts_pathlen += size + 128;
+ return(sp->fts_path = R(char, sp->fts_pathlen, sp->fts_path));
+}
diff --git a/private/posix/programs/bsdlib/getenv.c b/private/posix/programs/bsdlib/getenv.c
new file mode 100644
index 000000000..f6942b608
--- /dev/null
+++ b/private/posix/programs/bsdlib/getenv.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 1987 Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#ifdef DF_POSIX
+#include <bsdlib.h>
+#endif
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)getenv.c 5.8 (Berkeley) 2/23/91";
+#endif /* LIBC_SCCS and not lint */
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+
+/*
+ * getenv --
+ * Returns ptr to value associated with name, if any, else NULL.
+ */
+char *
+getenv(name)
+ const char *name;
+{
+ int offset;
+ char *_findenv();
+
+ return(_findenv(name, &offset));
+}
+
+/*
+ * _findenv --
+ * Returns pointer to value associated with name, if any, else NULL.
+ * Sets offset to be the offset of the name/value combination in the
+ * environmental array, for use by setenv(3) and unsetenv(3).
+ * Explicitly removes '=' in argument name.
+ *
+ * This routine *should* be a static; don't use it.
+ */
+char *
+_findenv(name, offset)
+ register char *name;
+ int *offset;
+{
+ extern char **environ;
+ register int len;
+ register char **P, *C;
+
+/*
+ for (P = environ; *P; ++P)
+ printf("environ X%sX\n", *P);
+*/
+
+ for (C = name, len = 0; *C && *C != '='; ++C, ++len);
+ for (P = environ; *P; ++P)
+ if (!strncmp(*P, name, len))
+ if (*(C = *P + len) == '=') {
+ *offset = P - environ;
+ return(++C);
+ }
+ return(NULL);
+}
diff --git a/private/posix/programs/bsdlib/getopt.c b/private/posix/programs/bsdlib/getopt.c
new file mode 100644
index 000000000..9aa1316fb
--- /dev/null
+++ b/private/posix/programs/bsdlib/getopt.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 1987 Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)getopt.c 4.13 (Berkeley) 2/23/91";
+#endif /* LIBC_SCCS and not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+extern char *index();
+extern char *rindex();
+
+/*
+ * get option letter from argument vector
+ */
+int opterr = 1, /* if error message should be printed */
+ optind = 1, /* index into parent argv vector */
+ optopt; /* character checked for validity */
+char *optarg; /* argument associated with option */
+
+#define BADCH (int)'?'
+#define EMSG ""
+
+int
+getopt(nargc, nargv, ostr)
+ int nargc;
+ char * const *nargv;
+ const char *ostr;
+{
+ static char *place = EMSG; /* option letter processing */
+ register char *oli; /* option letter list index */
+ char *p;
+
+ if (!*place) { /* update scanning pointer */
+ if (optind >= nargc || *(place = nargv[optind]) != '-') {
+ place = EMSG;
+ return(EOF);
+ }
+ if (place[1] && *++place == '-') { /* found "--" */
+ ++optind;
+ place = EMSG;
+ return(EOF);
+ }
+ } /* option letter okay? */
+ if ((optopt = (int)*place++) == (int)':' ||
+ !(oli = index(ostr, optopt))) {
+ /*
+ * if the user didn't specify '-' as an option,
+ * assume it means EOF.
+ */
+ if (optopt == (int)'-')
+ return(EOF);
+ if (!*place)
+ ++optind;
+ if (opterr) {
+ if (!(p = rindex(*nargv, '/')))
+ p = *nargv;
+ else
+ ++p;
+ (void)fprintf(stderr, "%s: illegal option -- %c\n",
+ p, optopt);
+ }
+ return(BADCH);
+ }
+ if (*++oli != ':') { /* don't need argument */
+ optarg = NULL;
+ if (!*place)
+ ++optind;
+ }
+ else { /* need an argument */
+ if (*place) /* no white space */
+ optarg = place;
+ else if (nargc <= ++optind) { /* no arg */
+ place = EMSG;
+ if (!(p = rindex(*nargv, '/')))
+ p = *nargv;
+ else
+ ++p;
+ if (opterr)
+ (void)fprintf(stderr,
+ "%s: option requires an argument -- %c\n",
+ p, optopt);
+ return(BADCH);
+ }
+ else /* white space */
+ optarg = nargv[optind];
+ place = EMSG;
+ ++optind;
+ }
+ return(optopt); /* dump back option letter */
+}
diff --git a/private/posix/programs/bsdlib/getwd.c b/private/posix/programs/bsdlib/getwd.c
new file mode 100644
index 000000000..56cb9ebcf
--- /dev/null
+++ b/private/posix/programs/bsdlib/getwd.c
@@ -0,0 +1,26 @@
+/*
+ * Getwd: Not Posix. Written in terms of Posix call, getcwd. AOJ
+ */
+
+#include <unistd.h>
+
+char *getwd (buf)
+register char
+ *buf;
+{
+ return getcwd (buf, 80);
+}
+/*
+main (argc, argv)
+char **argv;
+{
+ char
+ *c,
+ buf [64];
+
+ c = getwd (buf);
+ if (!c)
+ puts ("failed");
+ printf ("getwd %s\n", buf);
+}
+*/
diff --git a/private/posix/programs/bsdlib/index.c b/private/posix/programs/bsdlib/index.c
new file mode 100644
index 000000000..a65c3023d
--- /dev/null
+++ b/private/posix/programs/bsdlib/index.c
@@ -0,0 +1,13 @@
+/*
+ * Index: Not Posix. AOJ
+ */
+
+#include <stdio.h>
+
+char *index (s, c)
+char *s, c;
+{
+ for ( ; *s && *s != c; s++ );
+ return *s ? s : NULL;
+}
+
diff --git a/private/posix/programs/bsdlib/lstat.c b/private/posix/programs/bsdlib/lstat.c
new file mode 100644
index 000000000..69102c0d3
--- /dev/null
+++ b/private/posix/programs/bsdlib/lstat.c
@@ -0,0 +1,11 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+/*
+ * Lstat: Posix Implementation DF_AJ
+ */
+
+
+int lstat (char *path, struct stat *buf)
+{
+ return stat (path, buf);
+}
diff --git a/private/posix/programs/bsdlib/makefile b/private/posix/programs/bsdlib/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/posix/programs/bsdlib/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/posix/programs/bsdlib/mknod.c b/private/posix/programs/bsdlib/mknod.c
new file mode 100644
index 000000000..9adb84784
--- /dev/null
+++ b/private/posix/programs/bsdlib/mknod.c
@@ -0,0 +1,23 @@
+/*
+ * MKNOD: DF_MSS
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+
+int mknod (const char *path, mode_t mode, int dev)
+{
+ int ret;
+
+ if ((mode & S_IFMT) == S_IFDIR)
+ ret = mkdir(path, (mode & S_IFMT));
+ else if ((mode & S_IFMT) == S_IFIFO)
+ ret = mkfifo(path, (mode & S_IFMT));
+ else {
+ errno = EPERM;
+ ret = -1;
+ }
+ return ret;
+}
diff --git a/private/posix/programs/bsdlib/posixdbg.c b/private/posix/programs/bsdlib/posixdbg.c
new file mode 100644
index 000000000..745677dd1
--- /dev/null
+++ b/private/posix/programs/bsdlib/posixdbg.c
@@ -0,0 +1,2258 @@
+/*
+ posixdbg.c
+
+ POSIX debugging for those of us without debugging gear
+*/
+
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <sys/utsname.h>
+#include <time.h>
+#include <sys/times.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <utime.h>
+#include <termios.h>
+#include <setjmp.h>
+#include <grp.h>
+#include <pwd.h>
+
+#define NARGS 16 /* maximum number of arguments for execl* calls (for display purposes) */
+#define NENVS 32 /* maximum number of environment variables for execle call (for display purposes) */
+
+struct sigmap {
+ int signum;
+ const char *signame;
+};
+
+struct howmap {
+ int hownum;
+ const char *howname;
+};
+
+struct scnamemap {
+ int scnamenum;
+ const char *scnamename;
+};
+
+struct pcnamemap {
+ int pcnamenum;
+ const char *pcnamename;
+};
+
+struct cmdmap {
+ int cmdnum;
+ const char *cmdname;
+};
+
+struct whencemap {
+ int whencenum;
+ const char *whencename;
+};
+
+struct speedmap {
+ speed_t speednum;
+ const char *speedname;
+};
+
+struct optactmap {
+ int optactnum;
+ const char *optactname;
+};
+
+struct queuemap {
+ int queuenum;
+ const char *queuename;
+};
+
+struct actionmap {
+ int actionnum;
+ const char *actionname;
+};
+
+static const char *canonical_string (const char *string)
+{
+ static const char null[] = "NULL";
+ static char buf[BUFSIZ];
+ const char *ret;
+
+ if (string == NULL)
+ ret = null;
+ else
+ {
+ (void) sprintf(buf, "\"%s\"", string);
+ ret = (const char *) buf;
+ }
+ return ret;
+}
+
+char *options_string (int waitpid_options)
+{
+ static const char bitwise_or[] = "|";
+ static char buf[BUFSIZ];
+ char integer_string[11];
+
+ *buf = '\0';
+ if (waitpid_options & WNOHANG)
+ {
+ (void) strcat(buf, "WNOHANG");
+ waitpid_options &= ~WNOHANG;
+ }
+ if (waitpid_options & WUNTRACED)
+ {
+ if (*buf != '\0')
+ (void) strcat(buf, bitwise_or);
+ (void) strcat(buf, "WUNTRACED");
+ waitpid_options &= ~WUNTRACED;
+ }
+ if (waitpid_options)
+ {
+ if (*buf != '\0')
+ (void) strcat(buf, bitwise_or);
+ (void) sprintf(integer_string, "%#08X", (unsigned int) waitpid_options);
+ (void) strcat(buf, integer_string);
+ }
+ return buf;
+}
+
+char *signum_string (int function_signum)
+{
+ static const struct sigmap sigtable[] = {
+ { SIGABRT, "SIGABRT" }, { SIGALRM, "SIGALRM" }, { SIGFPE, "SIGFPE" }, { SIGHUP, "SIGHUP" },
+ { SIGILL, "SIGILL" }, { SIGINT, "SIGINT" }, { SIGKILL, "SIGKILL" }, { SIGPIPE, "SIGPIPE" },
+ { SIGQUIT, "SIGQUIT" }, { SIGSEGV, "SIGSEGV" }, { SIGTERM, "SIGTERM" }, { SIGUSR1, "SIGUSR1" },
+ { SIGUSR2, "SIGUSR2" }, { SIGCHLD, "SIGCHLD" }, { SIGCONT, "SIGCONT" }, { SIGSTOP, "SIGSTOP" },
+ { SIGTSTP, "SIGTSTP" }, { SIGTTIN, "SIGTTIN" }, { SIGTTOU, "SIGTTOU" }, { 0, NULL }
+ };
+ static char buf[BUFSIZ];
+ const struct sigmap *sigtblptr;
+
+ for (sigtblptr = sigtable; sigtblptr->signum != 0; ++sigtblptr)
+ if (sigtblptr->signum == function_signum)
+ break;
+ if (sigtblptr->signum == 0)
+ (void) sprintf(buf, "%d", function_signum);
+ else
+ (void) strcpy(buf, sigtblptr->signame);
+ return buf;
+}
+
+char *how_string (int sigprocmask_how)
+{
+ static const struct howmap howtable[] = {
+ { SIG_BLOCK, "SIG_BLOCK" }, { SIG_UNBLOCK, "SIG_UNBLOCK" }, { SIG_SETMASK, "SIG_SETMASK" },
+ { 0, NULL }
+ };
+ static char buf[BUFSIZ];
+ const struct howmap *howtblptr;
+
+ for (howtblptr = howtable; howtblptr->hownum != 0; ++howtblptr)
+ if (howtblptr->hownum == sigprocmask_how)
+ break;
+ if (howtblptr->hownum == 0)
+ (void) sprintf(buf, "%d", sigprocmask_how);
+ else
+ (void) strcpy(buf, howtblptr->howname);
+ return buf;
+}
+
+char *scname_string (int sysconf_name)
+{
+ static const struct scnamemap scnametable[] = {
+ { _SC_ARG_MAX, "_SC_ARG_MAX" }, { _SC_CHILD_MAX, "_SC_CHILD_MAX" },
+ { _SC_CLK_TCK, "_SC_CLK_TCK" }, { _SC_NGROUPS_MAX, "_SC_NGROUPS_MAX" },
+ { _SC_OPEN_MAX, "_SC_OPEN_MAX" }, { _SC_STREAM_MAX, "_SC_STREAM_MAX" },
+ { _SC_TZNAME_MAX, "_SC_TZNAME_MAX" }, { _SC_JOB_CONTROL, "_SC_JOB_CONTROL" },
+ { _SC_SAVED_IDS, "_SC_SAVED_IDS" }, { _SC_VERSION, "_SC_VERSION" },
+ { 0, NULL }
+ };
+ static char buf[BUFSIZ];
+ const struct scnamemap *scnametblptr;
+
+ for (scnametblptr = scnametable; scnametblptr->scnamenum != 0; ++scnametblptr)
+ if (scnametblptr->scnamenum == sysconf_name)
+ break;
+ if (scnametblptr->scnamenum == 0)
+ (void) sprintf(buf, "%d", sysconf_name);
+ else
+ (void) strcpy(buf, scnametblptr->scnamename);
+ return buf;
+}
+
+char *oflag_string (int open_oflag)
+{
+ static const char bitwise_or[] = "|";
+ static char buf[BUFSIZ];
+ char integer_string[11];
+
+ *buf = '\0';
+ if ((open_oflag & O_ACCMODE) == O_RDONLY)
+ {
+ (void) strcat(buf, "O_RDONLY");
+ open_oflag &= ~O_ACCMODE;
+ }
+ else if ((open_oflag & O_ACCMODE) == O_WRONLY)
+ {
+ (void) strcat(buf, "O_WRONLY");
+ open_oflag &= ~O_ACCMODE;
+ }
+ else if ((open_oflag & O_ACCMODE) == O_RDWR)
+ {
+ (void) strcat(buf, "O_RDWR");
+ open_oflag &= ~O_ACCMODE;
+ }
+ if (open_oflag & O_APPEND)
+ {
+ if (*buf != '\0')
+ (void) strcat(buf, bitwise_or);
+ (void) strcat(buf, "O_APPEND");
+ open_oflag &= ~O_APPEND;
+ }
+ if (open_oflag & O_CREAT)
+ {
+ if (*buf != '\0')
+ (void) strcat(buf, bitwise_or);
+ (void) strcat(buf, "O_CREAT");
+ open_oflag &= ~O_CREAT;
+ }
+ if (open_oflag & O_EXCL)
+ {
+ if (*buf != '\0')
+ (void) strcat(buf, bitwise_or);
+ (void) strcat(buf, "O_EXCL");
+ open_oflag &= ~O_EXCL;
+ }
+ if (open_oflag & O_NOCTTY)
+ {
+ if (*buf != '\0')
+ (void) strcat(buf, bitwise_or);
+ (void) strcat(buf, "O_NOCTTY");
+ open_oflag &= ~O_NOCTTY;
+ }
+ if (open_oflag & O_NONBLOCK)
+ {
+ if (*buf != '\0')
+ (void) strcat(buf, bitwise_or);
+ (void) strcat(buf, "O_NONBLOCK");
+ open_oflag &= ~O_NONBLOCK;
+ }
+ if (open_oflag & O_TRUNC)
+ {
+ if (*buf != '\0')
+ (void) strcat(buf, bitwise_or);
+ (void) strcat(buf, "O_TRUNC");
+ open_oflag &= ~O_TRUNC;
+ }
+ if (open_oflag)
+ {
+ if (*buf != '\0')
+ (void) strcat(buf, bitwise_or);
+ (void) sprintf(integer_string, "%#08X", (unsigned int) open_oflag);
+ (void) strcat(buf, integer_string);
+ }
+ return buf;
+}
+
+char *mode_string (mode_t function_mode)
+{
+ static const char bitwise_or[] = "|";
+ static char buf[BUFSIZ];
+ char mode_t_string[13];
+
+ *buf = '\0';
+ if (function_mode & S_IRUSR)
+ {
+ (void) strcat(buf, "S_IRUSR");
+ function_mode &= ~S_IRUSR;
+ }
+ if (function_mode & S_IWUSR)
+ {
+ if (*buf != '\0')
+ (void) strcat(buf, bitwise_or);
+ (void) strcat(buf, "S_IWUSR");
+ function_mode &= ~S_IWUSR;
+ }
+ if (function_mode & S_IXUSR)
+ {
+ if (*buf != '\0')
+ (void) strcat(buf, bitwise_or);
+ (void) strcat(buf, "S_IXUSR");
+ function_mode &= ~S_IXUSR;
+ }
+ if (function_mode & S_IRGRP)
+ {
+ if (*buf != '\0')
+ (void) strcat(buf, bitwise_or);
+ (void) strcat(buf, "S_IRGRP");
+ function_mode &= ~S_IRGRP;
+ }
+ if (function_mode & S_IWGRP)
+ {
+ if (*buf != '\0')
+ (void) strcat(buf, bitwise_or);
+ (void) strcat(buf, "S_IWGRP");
+ function_mode &= ~S_IWGRP;
+ }
+ if (function_mode & S_IXGRP)
+ {
+ if (*buf != '\0')
+ (void) strcat(buf, bitwise_or);
+ (void) strcat(buf, "S_IXGRP");
+ function_mode &= ~S_IXGRP;
+ }
+ if (function_mode & S_IROTH)
+ {
+ if (*buf != '\0')
+ (void) strcat(buf, bitwise_or);
+ (void) strcat(buf, "S_IROTH");
+ function_mode &= ~S_IROTH;
+ }
+ if (function_mode & S_IWOTH)
+ {
+ if (*buf != '\0')
+ (void) strcat(buf, bitwise_or);
+ (void) strcat(buf, "S_IWOTH");
+ function_mode &= ~S_IWOTH;
+ }
+ if (function_mode & S_IXOTH)
+ {
+ if (*buf != '\0')
+ (void) strcat(buf, bitwise_or);
+ (void) strcat(buf, "S_IXOTH");
+ function_mode &= ~S_IXOTH;
+ }
+ if (function_mode & S_ISUID)
+ {
+ if (*buf != '\0')
+ (void) strcat(buf, bitwise_or);
+ (void) strcat(buf, "S_ISUID");
+ function_mode &= ~S_ISUID;
+ }
+ if (function_mode & S_ISGID)
+ {
+ if (*buf != '\0')
+ (void) strcat(buf, bitwise_or);
+ (void) strcat(buf, "S_ISGID");
+ function_mode &= ~S_ISGID;
+ }
+ if (function_mode)
+ {
+ if (*buf != '\0')
+ (void) strcat(buf, bitwise_or);
+ (void) sprintf(mode_t_string, "%#11lo", (unsigned long) function_mode);
+ (void) strcat(buf, mode_t_string);
+ }
+ return buf;
+}
+
+char *amode_string (int access_amode)
+{
+ static const char bitwise_or[] = "|";
+ static char buf[BUFSIZ];
+ char integer_string[11];
+
+ *buf = '\0';
+ if (access_amode == F_OK)
+ {
+ (void) strcat(buf, "F_OK");
+ access_amode &= ~F_OK;
+ }
+ else
+ {
+ if (access_amode & R_OK)
+ {
+ (void) strcat(buf, "R_OK");
+ access_amode &= ~R_OK;
+ }
+ if (access_amode & W_OK)
+ {
+ if (*buf != '\0')
+ (void) strcat(buf, bitwise_or);
+ (void) strcat(buf, "W_OK");
+ access_amode &= ~W_OK;
+ }
+ if (access_amode & X_OK)
+ {
+ if (*buf != '\0')
+ (void) strcat(buf, bitwise_or);
+ (void) strcat(buf, "X_OK");
+ access_amode &= ~X_OK;
+ }
+ }
+ if (access_amode)
+ {
+ if (*buf != '\0')
+ (void) strcat(buf, bitwise_or);
+ (void) sprintf(integer_string, "%#08X", (unsigned int) access_amode);
+ (void) strcat(buf, integer_string);
+ }
+ return buf;
+}
+
+char *pcname_string (int function_name)
+{
+ static const struct pcnamemap pcnametable[] = {
+ { _PC_LINK_MAX, "_PC_LINK_MAX" }, { _PC_MAX_CANON, "_PC_MAX_CANON" },
+ { _PC_MAX_INPUT, "_PC_MAX_INPUT" }, { _PC_NAME_MAX, "_PC_NAME_MAX" },
+ { _PC_PATH_MAX, "_PC_PATH_MAX" }, { _PC_PIPE_BUF, "_PC_PIPE_BUF" },
+ { _PC_CHOWN_RESTRICTED, "_PC_CHOWN_RESTRICTED" }, { _PC_NO_TRUNC, "_PC_NO_TRUNC" },
+ { _PC_VDISABLE, "_PC_VDISABLE" }, { 0, NULL }
+ };
+ static char buf[BUFSIZ];
+ const struct pcnamemap *pcnametblptr;
+
+ for (pcnametblptr = pcnametable; pcnametblptr->pcnamenum != 0; ++pcnametblptr)
+ if (pcnametblptr->pcnamenum == function_name)
+ break;
+ if (pcnametblptr->pcnamenum == 0)
+ (void) sprintf(buf, "%d", function_name);
+ else
+ (void) strcpy(buf, pcnametblptr->pcnamename);
+ return buf;
+}
+
+char *cmd_string (int fcntl_cmd)
+{
+ static const struct cmdmap cmdtable[] = {
+ { F_DUPFD, "F_DUPFD" }, { F_GETFD, "F_GETFD" }, { F_SETFD, "F_SETFD" }, { F_GETFL, "F_GETFL" },
+ { F_SETFL, "F_SETFL" }, { F_GETLK, "F_GETLK" }, { F_SETLK, "F_SETLK" }, { F_SETLKW, "F_SETLKW" },
+ { 0, NULL }
+ };
+ static char buf[BUFSIZ];
+ const struct cmdmap *cmdtblptr;
+
+ for (cmdtblptr = cmdtable; cmdtblptr->cmdnum != 0; ++cmdtblptr)
+ if (cmdtblptr->cmdnum == fcntl_cmd)
+ break;
+ if (cmdtblptr->cmdnum == 0)
+ (void) sprintf(buf, "%d", fcntl_cmd);
+ else
+ (void) strcpy(buf, cmdtblptr->cmdname);
+ return buf;
+}
+
+char *whence_string (int lseek_whence)
+{
+ static const struct whencemap whencetable[] = {
+ { SEEK_SET, "SEEK_SET" }, { SEEK_CUR, "SEEK_CUR" }, { SEEK_END, "SEEK_END" }, { 0, NULL }
+ };
+ static char buf[BUFSIZ];
+ const struct whencemap *whencetblptr;
+
+ for (whencetblptr = whencetable; whencetblptr->whencenum != 0; ++whencetblptr)
+ if (whencetblptr->whencenum == lseek_whence)
+ break;
+ if (whencetblptr->whencenum == 0)
+ (void) sprintf(buf, "%d", lseek_whence);
+ else
+ (void) strcpy(buf, whencetblptr->whencename);
+ return buf;
+}
+
+char *speed_string (speed_t function_speed)
+{
+ static const struct speedmap speedtable[] = {
+ { B0, "B0" }, { B50, "B50" }, { B75, "B75" }, { B110, "B110" }, { B134, "B134" },
+ { B150, "B150" }, { B200, "B200" }, { B300, "B300" }, { B600, "B600" }, { B1200, "B1200" },
+ { B1800, "B1800" }, { B2400, "B2400" }, { B4800, "B4800" }, { B9600, "B9600" }, { B19200, "B19200" },
+ { B38400, "B38400" }, { 0, NULL }
+ };
+ static char buf[BUFSIZ];
+ const struct speedmap *speedtblptr;
+
+ for (speedtblptr = speedtable; speedtblptr->speednum != 0; ++speedtblptr)
+ if (speedtblptr->speednum == function_speed)
+ break;
+ if (speedtblptr->speednum == 0)
+ (void) sprintf(buf, "%d", function_speed);
+ else
+ (void) strcpy(buf, speedtblptr->speedname);
+ return buf;
+}
+
+char *optact_string (int tcsetattr_optact)
+{
+ static const struct optactmap optacttable[] = {
+ { TCSANOW, "TCSANOW" }, { TCSADRAIN, "TCSADRAIN" }, { TCSAFLUSH, "TCSAFLUSH" }, { 0, NULL }
+ };
+ static char buf[BUFSIZ];
+ const struct optactmap *optacttblptr;
+
+ for (optacttblptr = optacttable; optacttblptr->optactnum != 0; ++optacttblptr)
+ if (optacttblptr->optactnum == tcsetattr_optact)
+ break;
+ if (optacttblptr->optactnum == 0)
+ (void) sprintf(buf, "%d", tcsetattr_optact);
+ else
+ (void) strcpy(buf, optacttblptr->optactname);
+ return buf;
+}
+
+char *queue_string (int tcflush_queue)
+{
+ static const struct queuemap queuetable[] = {
+ { TCIFLUSH, "TCIFLUSH" }, { TCOFLUSH, "TCOFLUSH" }, { TCIOFLUSH, "TCIOFLUSH" }, { 0, NULL }
+ };
+ static char buf[BUFSIZ];
+ const struct queuemap *queuetblptr;
+
+ for (queuetblptr = queuetable; queuetblptr->queuenum != 0; ++queuetblptr)
+ if (queuetblptr->queuenum == tcflush_queue)
+ break;
+ if (queuetblptr->queuenum == 0)
+ (void) sprintf(buf, "%d", tcflush_queue);
+ else
+ (void) strcpy(buf, queuetblptr->queuename);
+ return buf;
+}
+
+char *action_string (int tcflow_action)
+{
+ static const struct actionmap actiontable[] = {
+ { TCOOFF, "TCOOFF" }, { TCOON, "TCOON" }, { TCIOFF, "TCIOFF" }, { TCION, "TCION" }, { 0, NULL }
+ };
+ static char buf[BUFSIZ];
+ const struct actionmap *actiontblptr;
+
+ for (actiontblptr = actiontable; actiontblptr->actionnum != 0; ++actiontblptr)
+ if (actiontblptr->actionnum == tcflow_action)
+ break;
+ if (actiontblptr->actionnum == 0)
+ (void) sprintf(buf, "%d", tcflow_action);
+ else
+ (void) strcpy(buf, actiontblptr->actionname);
+ return buf;
+}
+
+/* Section 3: Process Primitives */
+
+pid_t posix_debug_fork (void)
+{
+ pid_t ret;
+ int err;
+
+ (void) fprintf(stderr, "entering fork()\n");
+ (void) fflush(stderr);
+ ret = fork();
+ err = errno;
+ (void) fprintf(stderr, "exiting fork with %ld", (long) ret);
+ if (ret == (pid_t) -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_execl (const char *path, const char *arg, ...)
+{
+ va_list va;
+ int ret, err, i;
+ const char *args[NARGS];
+
+ va_start(va, arg);
+ (void) fprintf(stderr, "entering execl(%s, %s", canonical_string(path), canonical_string(arg));
+ for (i = 0; i < NARGS; ++i)
+ {
+ args[i] = va_arg(va, const char *);
+ (void) fprintf(stderr, ", %s", canonical_string(args[i]));
+ if (args[i] == NULL)
+ break;
+ }
+ va_end(va);
+ if (i == NARGS)
+ (void) fprintf(stderr, ", ..., NULL");
+ (void) fprintf(stderr, ")\n");
+ (void) fflush(stderr);
+ if (i == 0)
+ ret = execl(path, arg, NULL);
+ else if (i == 1)
+ ret = execl(path, arg, args[0], NULL);
+ else if (i == 2)
+ ret = execl(path, arg, args[0], args[1], NULL);
+ else if (i == 3)
+ ret = execl(path, arg, args[0], args[1], args[2], NULL);
+ else if (i == 4)
+ ret = execl(path, arg, args[0], args[1], args[2], args[3], NULL);
+ else if (i == 5)
+ ret = execl(path, arg, args[0], args[1], args[2], args[3], args[4], NULL);
+ else if (i == 6)
+ ret = execl(path, arg, args[0], args[1], args[2], args[3], args[4], args[5], NULL);
+ else if (i == 7)
+ ret = execl(path, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], NULL);
+ else if (i == 8)
+ ret = execl(path, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], NULL);
+ else if (i == 9)
+ ret = execl(path, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], NULL);
+ else if (i == 10)
+ ret = execl(path, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9],
+ NULL);
+ else if (i == 11)
+ ret = execl(path, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9],
+ args[10], NULL);
+ else if (i == 12)
+ ret = execl(path, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9],
+ args[10], args[11], NULL);
+ else if (i == 13)
+ ret = execl(path, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9],
+ args[10], args[11], args[12], NULL);
+ else if (i == 14)
+ ret = execl(path, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9],
+ args[10], args[11], args[12], args[13], NULL);
+ else if (i == 15)
+ ret = execl(path, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9],
+ args[10], args[11], args[12], args[13], args[14], NULL);
+ else
+ ret = execl(path, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9],
+ args[10], args[11], args[12], args[13], args[14], args[15], NULL);
+/*
+ If NARGS changes from its original value of 16, add or subtract "if" cases according to this pattern.
+*/
+ err = errno;
+ (void) fprintf(stderr, "exiting execl with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_execv (const char *path, char * const argv[])
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering execv(%s, %p)\n", canonical_string(path), argv);
+ (void) fflush(stderr);
+ ret = execv(path, argv);
+ err = errno;
+ (void) fprintf(stderr, "exiting execv with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_execle (const char *path, const char *arg, ...)
+{
+ va_list va;
+ int ret, err, i, j;
+ const char *args[NARGS];
+ const char *envp[NENVS]; /* POSIX says it should be char * const [], but that's unassignable! */
+
+ (void) fprintf(stderr, "entering execle(%s, %s", canonical_string(path), canonical_string(arg));
+ for (i = 0; i < NARGS; ++i)
+ {
+ args[i] = va_arg(va, const char *);
+ (void) fprintf(stderr, ", %s", canonical_string(args[i]));
+ if (args[i] == NULL)
+ break;
+ }
+ if (i == NARGS)
+ {
+ while (va_arg(va, const char *) != NULL)
+ ;
+ (void) fprintf(stderr, ", ..., NULL");
+ }
+ for (j = 0; j < NENVS; ++j)
+ {
+ envp[j] = va_arg(va, char * const);
+ (void) fprintf(stderr, ", %s", canonical_string(envp[j]));
+ if (envp[j] == NULL)
+ break;
+ }
+ if (j == NENVS)
+ {
+ envp[NENVS - 1] = NULL;
+ (void) fprintf(stderr, ", ..., NULL");
+ }
+ va_end(va);
+ (void) fprintf(stderr, ")\n");
+ (void) fflush(stderr);
+ if (i == 0)
+ ret = execle(path, arg, NULL, envp);
+ else if (i == 1)
+ ret = execle(path, arg, args[0], NULL, envp);
+ else if (i == 2)
+ ret = execle(path, arg, args[0], args[1], NULL, envp);
+ else if (i == 3)
+ ret = execle(path, arg, args[0], args[1], args[2], NULL, envp);
+ else if (i == 4)
+ ret = execle(path, arg, args[0], args[1], args[2], args[3], NULL, envp);
+ else if (i == 5)
+ ret = execle(path, arg, args[0], args[1], args[2], args[3], args[4], NULL, envp);
+ else if (i == 6)
+ ret = execle(path, arg, args[0], args[1], args[2], args[3], args[4], args[5], NULL, envp);
+ else if (i == 7)
+ ret = execle(path, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], NULL, envp);
+ else if (i == 8)
+ ret = execle(path, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], NULL, envp);
+ else if (i == 9)
+ ret = execle(path, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], NULL,
+ envp);
+ else if (i == 10)
+ ret = execle(path, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9],
+ NULL, envp);
+ else if (i == 11)
+ ret = execle(path, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9],
+ args[10], NULL, envp);
+ else if (i == 12)
+ ret = execle(path, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9],
+ args[10], args[11], NULL, envp);
+ else if (i == 13)
+ ret = execle(path, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9],
+ args[10], args[11], args[12], NULL, envp);
+ else if (i == 14)
+ ret = execle(path, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9],
+ args[10], args[11], args[12], args[13], NULL, envp);
+ else if (i == 15)
+ ret = execle(path, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9],
+ args[10], args[11], args[12], args[13], args[14], NULL, envp);
+ else
+ ret = execle(path, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9],
+ args[10], args[11], args[12], args[13], args[14], args[15], NULL, envp);
+/*
+ If NARGS changes from its original value of 16, add or subtract "if" cases according to this pattern.
+*/
+ err = errno;
+ (void) fprintf(stderr, "exiting execle with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_execve (const char *path, char * const argv[], char * const envp[])
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering execve(%s, %p, %p)\n", canonical_string(path), argv, envp);
+ (void) fflush(stderr);
+ ret = execve(path, argv, envp);
+ err = errno;
+ (void) fprintf(stderr, "exiting execve with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_execlp (const char *file, const char *arg, ...)
+{
+ va_list va;
+ int ret, err, i;
+ const char *args[NARGS];
+
+ va_start(va, arg);
+ (void) fprintf(stderr, "entering execlp(%s, %s", canonical_string(file), canonical_string(arg));
+ for (i = 0; i < NARGS; ++i)
+ {
+ args[i] = va_arg(va, const char *);
+ (void) fprintf(stderr, ", %s", canonical_string(args[i]));
+ if (args[i] == NULL)
+ break;
+ }
+ va_end(va);
+ if (i == NARGS)
+ (void) fprintf(stderr, ", ..., NULL");
+ (void) fprintf(stderr, ")\n");
+ (void) fflush(stderr);
+ if (i == 0)
+ ret = execlp(file, arg, NULL);
+ else if (i == 1)
+ ret = execlp(file, arg, args[0], NULL);
+ else if (i == 2)
+ ret = execlp(file, arg, args[0], args[1], NULL);
+ else if (i == 3)
+ ret = execlp(file, arg, args[0], args[1], args[2], NULL);
+ else if (i == 4)
+ ret = execlp(file, arg, args[0], args[1], args[2], args[3], NULL);
+ else if (i == 5)
+ ret = execlp(file, arg, args[0], args[1], args[2], args[3], args[4], NULL);
+ else if (i == 6)
+ ret = execlp(file, arg, args[0], args[1], args[2], args[3], args[4], args[5], NULL);
+ else if (i == 7)
+ ret = execlp(file, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], NULL);
+ else if (i == 8)
+ ret = execlp(file, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], NULL);
+ else if (i == 9)
+ ret = execlp(file, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], NULL);
+ else if (i == 10)
+ ret = execlp(file, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9],
+ NULL);
+ else if (i == 11)
+ ret = execlp(file, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9],
+ args[10], NULL);
+ else if (i == 12)
+ ret = execlp(file, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9],
+ args[10], args[11], NULL);
+ else if (i == 13)
+ ret = execlp(file, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9],
+ args[10], args[11], args[12], NULL);
+ else if (i == 14)
+ ret = execlp(file, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9],
+ args[10], args[11], args[12], args[13], NULL);
+ else if (i == 15)
+ ret = execlp(file, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9],
+ args[10], args[11], args[12], args[13], args[14], NULL);
+ else
+ ret = execlp(file, arg, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9],
+ args[10], args[11], args[12], args[13], args[14], args[15], NULL);
+/*
+ If NARGS changes from its original value of 16, add or subtract "if" cases according to this pattern.
+*/
+ err = errno;
+ (void) fprintf(stderr, "exiting execlp with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_execvp (const char *file, char * const argv[])
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering execvp(%s, %p)\n", canonical_string(file), argv);
+ (void) fflush(stderr);
+ ret = execvp(file, argv);
+ err = errno;
+ (void) fprintf(stderr, "exiting execvp with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+pid_t posix_debug_wait (int *statloc)
+{
+ pid_t ret;
+ int err;
+
+ (void) fprintf(stderr, "entering wait(%p)\n", statloc);
+ (void) fflush(stderr);
+ ret = wait(statloc);
+ err = errno;
+ (void) fprintf(stderr, "exiting wait with %ld", (long) ret);
+ if (ret == (pid_t) -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+pid_t posix_debug_waitpid (pid_t pid, int *statloc, int options)
+{
+ pid_t ret;
+ int err;
+
+ (void) fprintf(stderr, "entering waitpid(%ld, %p, %s)\n", (long) pid, statloc, options_string(options));
+ (void) fflush(stderr);
+ ret = waitpid(pid, statloc, options);
+ err = errno;
+ (void) fprintf(stderr, "exiting waitpid with %ld", (long) ret);
+ if (ret == (pid_t) -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+void posix_debug__exit (int status)
+{
+ int err, saved_errno;
+
+ (void) fprintf(stderr, "entering _exit(%d)\n", status);
+ (void) fflush(stderr);
+ saved_errno = errno;
+ _exit(status);
+ err = errno;
+ (void) fprintf(stderr, "exiting _exit");
+ if (saved_errno != err)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+}
+
+int posix_debug_kill (pid_t pid, int sig)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering kill(%ld, %s)\n", (long) pid, signum_string(sig));
+ (void) fflush(stderr);
+ ret = kill(pid, sig);
+ err = errno;
+ (void) fprintf(stderr, "exiting kill with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_sigemptyset (sigset_t *set)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering sigemptyset(%p)\n", set);
+ (void) fflush(stderr);
+ ret = sigemptyset(set);
+ err = errno;
+ (void) fprintf(stderr, "exiting sigemptyset with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_sigfillset (sigset_t *set)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering sigfillset(%p)\n", set);
+ (void) fflush(stderr);
+ ret = sigfillset(set);
+ err = errno;
+ (void) fprintf(stderr, "exiting sigfillset with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_sigaddset (sigset_t *set, int signo)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering sigaddset(%p, %s)\n", set, signum_string(signo));
+ (void) fflush(stderr);
+ ret = sigaddset(set, signo);
+ err = errno;
+ (void) fprintf(stderr, "exiting sigaddset with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_sigdelset (sigset_t *set, int signo)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering sigdelset(%p, %s)\n", set, signum_string(signo));
+ (void) fflush(stderr);
+ ret = sigdelset(set, signo);
+ err = errno;
+ (void) fprintf(stderr, "exiting sigdelset with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_sigismember (const sigset_t *set, int signo)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering sigismember(%p, %s)\n", set, signum_string(signo));
+ (void) fflush(stderr);
+ ret = sigismember(set, signo);
+ err = errno;
+ (void) fprintf(stderr, "exiting sigismember with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_sigaction (int sig, const struct sigaction *act, struct sigaction *oact)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering sigaction(%s, %p, %p)\n", signum_string(sig), act, oact);
+ (void) fflush(stderr);
+ ret = sigaction(sig, act, oact);
+ err = errno;
+ (void) fprintf(stderr, "exiting sigaction with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_sigprocmask (int how, const sigset_t *set, sigset_t *oset)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering sigprocmask(%s, %p, %p)\n", how_string(how), set, oset);
+ (void) fflush(stderr);
+ ret = sigprocmask(how, set, oset);
+ err = errno;
+ (void) fprintf(stderr, "exiting sigprocmask with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_sigpending (sigset_t *set)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering sigpending(%p)\n", set);
+ (void) fflush(stderr);
+ ret = sigpending(set);
+ err = errno;
+ (void) fprintf(stderr, "exiting sigpending with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_sigsuspend (const sigset_t *sigmask)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering sigsuspend(%p)\n", sigmask);
+ (void) fflush(stderr);
+ ret = sigsuspend(sigmask);
+ err = errno;
+ (void) fprintf(stderr, "exiting sigsuspend with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+unsigned int posix_debug_alarm (unsigned int seconds)
+{
+ unsigned int ret;
+
+ (void) fprintf(stderr, "entering alarm(%u)\n", seconds);
+ (void) fflush(stderr);
+ ret = alarm(seconds);
+ (void) fprintf(stderr, "exiting alarm with %u\n", ret);
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_pause (void)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering pause()\n");
+ (void) fflush(stderr);
+ ret = pause();
+ err = errno;
+ (void) fprintf(stderr, "exiting pause with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+unsigned int posix_debug_sleep (unsigned int seconds)
+{
+ unsigned int ret;
+
+ (void) fprintf(stderr, "entering sleep(%u)\n", seconds);
+ (void) fflush(stderr);
+ ret = sleep(seconds);
+ (void) fprintf(stderr, "exiting sleep with %u\n", ret);
+ (void) fflush(stderr);
+ return ret;
+}
+
+/* Section 4: Process Environment */
+
+pid_t posix_debug_getpid (void)
+{
+ pid_t ret;
+
+ (void) fprintf(stderr, "entering getpid()\n");
+ (void) fflush(stderr);
+ ret = getpid();
+ (void) fprintf(stderr, "exiting getpid with %ld\n", (long) ret);
+ (void) fflush(stderr);
+ return ret;
+}
+
+pid_t posix_debug_getppid (void)
+{
+ pid_t ret;
+
+ (void) fprintf(stderr, "entering getppid()\n");
+ (void) fflush(stderr);
+ ret = getppid();
+ (void) fprintf(stderr, "exiting getppid with %ld\n", (long) ret);
+ (void) fflush(stderr);
+ return ret;
+}
+
+uid_t posix_debug_getuid (void)
+{
+ uid_t ret;
+
+ (void) fprintf(stderr, "entering getuid()\n");
+ (void) fflush(stderr);
+ ret = getuid();
+ (void) fprintf(stderr, "exiting getuid with %lu\n", (unsigned long) ret);
+ (void) fflush(stderr);
+ return ret;
+}
+
+uid_t posix_debug_geteuid (void)
+{
+ uid_t ret;
+
+ (void) fprintf(stderr, "entering geteuid()\n");
+ (void) fflush(stderr);
+ ret = geteuid();
+ (void) fprintf(stderr, "exiting geteuid with %lu\n", (unsigned long) ret);
+ (void) fflush(stderr);
+ return ret;
+}
+
+gid_t posix_debug_getgid (void)
+{
+ gid_t ret;
+
+ (void) fprintf(stderr, "entering getgid()\n");
+ (void) fflush(stderr);
+ ret = getgid();
+ (void) fprintf(stderr, "exiting getgid with %lu\n", (unsigned long) ret);
+ (void) fflush(stderr);
+ return ret;
+}
+
+gid_t posix_debug_getegid (void)
+{
+ gid_t ret;
+
+ (void) fprintf(stderr, "entering getegid()\n");
+ (void) fflush(stderr);
+ ret = getegid();
+ (void) fprintf(stderr, "exiting getegid with %lu\n", (unsigned long) ret);
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_setuid (uid_t uid)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering setuid(%lu)\n", (unsigned long) uid);
+ (void) fflush(stderr);
+ ret = setuid(uid);
+ err = errno;
+ (void) fprintf(stderr, "exiting setuid with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_setgid (gid_t gid)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering setgid(%lu)\n", (unsigned long) gid);
+ (void) fflush(stderr);
+ ret = setgid(gid);
+ err = errno;
+ (void) fprintf(stderr, "exiting setgid with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_getgroups (int gidsetsize, gid_t grouplist[])
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering getgroups(%d, %p)\n", gidsetsize, grouplist);
+ (void) fflush(stderr);
+ ret = getgroups(gidsetsize, grouplist);
+ err = errno;
+ (void) fprintf(stderr, "exiting getgroups with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+char *posix_debug_getlogin (void)
+{
+ char *ret;
+
+ (void) fprintf(stderr, "entering getlogin()\n");
+ (void) fflush(stderr);
+ ret = getlogin();
+ (void) fprintf(stderr, "exiting getlogin with %s\n", canonical_string(ret));
+ (void) fflush(stderr);
+ return ret;
+}
+
+pid_t posix_debug_getpgrp (void)
+{
+ pid_t ret;
+
+ (void) fprintf(stderr, "entering getpgrp()\n");
+ (void) fflush(stderr);
+ ret = getpgrp();
+ (void) fprintf(stderr, "exiting getpgrp with %ld\n", (long) ret);
+ (void) fflush(stderr);
+ return ret;
+}
+
+pid_t posix_debug_setsid (void)
+{
+ pid_t ret;
+ int err;
+
+ (void) fprintf(stderr, "entering setsid()\n");
+ (void) fflush(stderr);
+ ret = setsid();
+ err = errno;
+ (void) fprintf(stderr, "exiting setsid with %ld", (long) ret);
+ if (ret == (pid_t) -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_setpgid (pid_t pid, pid_t pgid)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering setpgid(%ld, %ld)\n", (long) pid, (long) pgid);
+ (void) fflush(stderr);
+ ret = setpgid(pid, pgid);
+ err = errno;
+ (void) fprintf(stderr, "exiting setpgid with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_uname (struct utsname *name)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering uname(%p)\n", name);
+ (void) fflush(stderr);
+ ret = uname(name);
+ err = errno;
+ (void) fprintf(stderr, "exiting uname with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+time_t posix_debug_time (time_t *tloc)
+{
+ time_t ret;
+ int err;
+
+ (void) fprintf(stderr, "entering time(%p)\n", tloc);
+ (void) fflush(stderr);
+ ret = time(tloc);
+ err = errno;
+ (void) fprintf(stderr, "exiting time with %ld", (long) ret);
+ if (ret == (time_t) -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+clock_t posix_debug_times (struct tms *buffer)
+{
+ clock_t ret;
+ int err;
+
+ (void) fprintf(stderr, "entering times(%p)\n", buffer);
+ (void) fflush(stderr);
+ ret = times(buffer);
+ err = errno;
+ (void) fprintf(stderr, "exiting times with %ld", (long) ret);
+ if (ret == (clock_t) -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+char *posix_debug_getenv (const char *name)
+{
+ char *ret;
+
+ (void) fprintf(stderr, "entering getenv(%s)\n", canonical_string(name));
+ (void) fflush(stderr);
+ ret = getenv(name);
+ (void) fprintf(stderr, "exiting getenv with %s\n", canonical_string(ret));
+ (void) fflush(stderr);
+ return ret;
+}
+
+char *posix_debug_ctermid (char *s)
+{
+ char *ret;
+
+ (void) fprintf(stderr, "entering ctermid(%s)\n", canonical_string(s));
+ (void) fflush(stderr);
+ ret = ctermid(s);
+ (void) fprintf(stderr, "exiting ctermid with %s\n", canonical_string(ret));
+ (void) fflush(stderr);
+ return ret;
+}
+
+char *posix_debug_ttyname (int fildes)
+{
+ char *ret;
+
+ (void) fprintf(stderr, "entering ttyname(%d)\n", fildes);
+ (void) fflush(stderr);
+ ret = ttyname(fildes);
+ (void) fprintf(stderr, "exiting ttyname with %s\n", canonical_string(ret));
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_isatty (int fildes)
+{
+ int ret;
+
+ (void) fprintf(stderr, "entering isatty(%d)\n", fildes);
+ (void) fflush(stderr);
+ ret = isatty(fildes);
+ (void) fprintf(stderr, "exiting isatty with %d\n", ret);
+ (void) fflush(stderr);
+ return ret;
+}
+
+long posix_debug_sysconf (int name)
+{
+ long ret;
+ int err, saved_errno;
+
+ (void) fprintf(stderr, "entering sysconf(%s)\n", scname_string(name));
+ (void) fflush(stderr);
+ saved_errno = errno;
+ ret = sysconf(name);
+ err = errno;
+ (void) fprintf(stderr, "exiting sysconf with %ld", (long) ret);
+ if (ret == (long) -1 && saved_errno != err)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+/* Section 5: Files and Directories */
+
+DIR *posix_debug_opendir (const char *dirname)
+{
+ DIR *ret;
+ int err;
+
+ (void) fprintf(stderr, "entering opendir(%s)\n", canonical_string(dirname));
+ (void) fflush(stderr);
+ ret = opendir(dirname);
+ err = errno;
+ (void) fprintf(stderr, "exiting opendir with %p", ret);
+ if (ret == NULL)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+struct dirent *posix_debug_readdir (DIR *dirp)
+{
+ struct dirent *ret;
+ int err;
+
+ (void) fprintf(stderr, "entering readdir(%p)\n", dirp);
+ (void) fflush(stderr);
+ ret = readdir(dirp);
+ err = errno;
+ (void) fprintf(stderr, "exiting readdir with %p", ret);
+ if (ret == NULL)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+void posix_debug_rewinddir (DIR *dirp)
+{
+ int err, saved_errno;
+
+ (void) fprintf(stderr, "entering rewinddir(%p)\n", dirp);
+ (void) fflush(stderr);
+ saved_errno = errno;
+ rewinddir(dirp);
+ err = errno;
+ (void) fprintf(stderr, "exiting rewinddir");
+ if (saved_errno != err)
+ (void) fprintf(stderr, "; errno: %d\n", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+}
+
+int posix_debug_closedir (DIR *dirp)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering closedir(%p)\n", dirp);
+ (void) fflush(stderr);
+ ret = closedir(dirp);
+ err = errno;
+ (void) fprintf(stderr, "exiting closedir with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_chdir (const char *path)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering chdir(%s)\n", canonical_string(path));
+ (void) fflush(stderr);
+ ret = chdir(path);
+ err = errno;
+ (void) fprintf(stderr, "exiting chdir with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+char *posix_debug_getcwd (char *buf, size_t size)
+{
+ char *ret;
+ int err;
+
+ (void) fprintf(stderr, "entering getcwd(%p, %lu)\n", buf, (unsigned long) size);
+ (void) fflush(stderr);
+ ret = getcwd(buf, size);
+ err = errno;
+ (void) fprintf(stderr, "exiting getcwd with %s", canonical_string(ret));
+ if (ret == NULL)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_open (const char *path, int oflag, ...)
+{
+ va_list va;
+ int ret, err;
+ mode_t mode;
+
+ va_start(va, oflag);
+ (void) fprintf(stderr, "entering open(%s, %s", canonical_string(path), oflag_string(oflag));
+ if (oflag & O_CREAT)
+ {
+ mode = va_arg(va, mode_t);
+ (void) fprintf(stderr, ", %s", mode_string(mode));
+ }
+ va_end(va);
+ (void) fprintf(stderr, ")\n");
+ (void) fflush(stderr);
+ if (oflag & O_CREAT)
+ ret = open(path, oflag, mode);
+ else
+ ret = open(path, oflag);
+ err = errno;
+ (void) fprintf(stderr, "exiting open with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_creat (const char *path, mode_t mode)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering creat(%s, %s)\n", canonical_string(path), mode_string(mode));
+ (void) fflush(stderr);
+ ret = creat(path, mode);
+ err = errno;
+ (void) fprintf(stderr, "exiting creat with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+mode_t posix_debug_umask (mode_t cmask)
+{
+ mode_t ret;
+
+ (void) fprintf(stderr, "entering umask(%s)\n", mode_string(cmask));
+ (void) fflush(stderr);
+ ret = umask(cmask);
+ (void) fprintf(stderr, "exiting umask with %s\n", mode_string(ret));
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_link (const char *existing, const char *new)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering link(%s, %s)\n", canonical_string(existing), canonical_string(new));
+ (void) fflush(stderr);
+ ret = link(existing, new);
+ err = errno;
+ (void) fprintf(stderr, "exiting link with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_mkdir (const char *path, mode_t mode)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering mkdir(%s, %s)\n", canonical_string(path), mode_string(mode));
+ (void) fflush(stderr);
+ ret = mkdir(path, mode);
+ err = errno;
+ (void) fprintf(stderr, "exiting mkdir with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_mkfifo (const char *path, mode_t mode)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering mkfifo(%s, %s)\n", canonical_string(path), mode_string(mode));
+ (void) fflush(stderr);
+ ret = mkfifo(path, mode);
+ err = errno;
+ (void) fprintf(stderr, "exiting mkfifo with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_unlink (const char *path)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering unlink(%s)\n", canonical_string(path));
+ (void) fflush(stderr);
+ ret = unlink(path);
+ err = errno;
+ (void) fprintf(stderr, "exiting unlink with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_rmdir (const char *path)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering rmdir(%s)\n", canonical_string(path));
+ (void) fflush(stderr);
+ ret = rmdir(path);
+ err = errno;
+ (void) fprintf(stderr, "exiting rmdir with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_rename (const char *old, const char *new)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering rename(%s, %s)\n", canonical_string(old), canonical_string(new));
+ (void) fflush(stderr);
+ ret = rename(old, new);
+ err = errno;
+ (void) fprintf(stderr, "exiting rename with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_stat (const char *path, struct stat *buf)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering stat(%s, %p)\n", canonical_string(path), buf);
+ (void) fflush(stderr);
+ ret = stat(path, buf);
+ err = errno;
+ (void) fprintf(stderr, "exiting stat with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_fstat (int fildes, struct stat *buf)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering fstat(%d, %p)\n", fildes, buf);
+ (void) fflush(stderr);
+ ret = fstat(fildes, buf);
+ err = errno;
+ (void) fprintf(stderr, "exiting fstat with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_access (const char *path, int amode)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering access(%s, %s)\n", canonical_string(path), amode_string(amode));
+ (void) fflush(stderr);
+ ret = access(path, amode);
+ err = errno;
+ (void) fprintf(stderr, "exiting access with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_chmod (const char *path, mode_t mode)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering chmod(%s, %s)\n", canonical_string(path), mode_string(mode));
+ (void) fflush(stderr);
+ ret = chmod(path, mode);
+ err = errno;
+ (void) fprintf(stderr, "exiting chmod with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_chown (const char *path, uid_t owner, gid_t group)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering chown(%s, %lu, %lu)\n",
+ canonical_string(path), (unsigned long) owner, (unsigned long) group);
+ (void) fflush(stderr);
+ ret = chown(path, owner, group);
+ err = errno;
+ (void) fprintf(stderr, "exiting chown with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_utime (const char *path, const struct utimbuf *times)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering utime(%s, %p)\n", canonical_string(path), times);
+ (void) fflush(stderr);
+ ret = utime(path, times);
+ err = errno;
+ (void) fprintf(stderr, "exiting utime with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+long posix_debug_pathconf (const char *path, int name)
+{
+ long ret;
+ int err, saved_errno;
+
+ (void) fprintf(stderr, "entering pathconf(%s, %s)\n", canonical_string(path), pcname_string(name));
+ (void) fflush(stderr);
+ saved_errno = errno;
+ ret = pathconf(path, name);
+ err = errno;
+ (void) fprintf(stderr, "exiting pathconf with %ld", (long) ret);
+ if (ret == (long) -1 && saved_errno != err)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+long posix_debug_fpathconf (int fildes, int name)
+{
+ long ret;
+ int err, saved_errno;
+
+ (void) fprintf(stderr, "entering fpathconf(%d, %s)\n", fildes, pcname_string(name));
+ (void) fflush(stderr);
+ saved_errno = errno;
+ ret = fpathconf(fildes, name);
+ err = errno;
+ (void) fprintf(stderr, "exiting fpathconf with %ld", (long) ret);
+ if (ret == (long) -1 && saved_errno != err)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+/* Section 6: Input and Output Primitives */
+
+int posix_debug_pipe (int fildes[2])
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering pipe(%p)\n", fildes);
+ (void) fflush(stderr);
+ ret = pipe(fildes);
+ err = errno;
+ (void) fprintf(stderr, "exiting pipe with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_dup (int fildes)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering dup(%d)\n", fildes);
+ (void) fflush(stderr);
+ ret = dup(fildes);
+ err = errno;
+ (void) fprintf(stderr, "exiting dup with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_dup2 (int fildes, int fildes2)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering dup2(%d, %d)\n", fildes, fildes2);
+ (void) fflush(stderr);
+ ret = dup2(fildes, fildes2);
+ err = errno;
+ (void) fprintf(stderr, "exiting dup2 with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_close (int fildes)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering close(%d)\n", fildes);
+ (void) fflush(stderr);
+ ret = close(fildes);
+ err = errno;
+ (void) fprintf(stderr, "exiting close with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+ssize_t posix_debug_read (int fildes, void *buf, size_t nbyte)
+{
+ ssize_t ret;
+ int err;
+
+ (void) fprintf(stderr, "entering read(%d, %p, %lu)\n", fildes, buf, (unsigned long) nbyte);
+ (void) fflush(stderr);
+ ret = read(fildes, buf, nbyte);
+ err = errno;
+ (void) fprintf(stderr, "exiting read with %ld", (long) ret);
+ if (ret == (ssize_t) -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+ssize_t posix_debug_write (int fildes, const void *buf, size_t nbyte)
+{
+ ssize_t ret;
+ int err;
+
+ (void) fprintf(stderr, "entering write(%d, %p, %lu)\n", fildes, buf, (unsigned long) nbyte);
+ (void) fflush(stderr);
+ ret = write(fildes, buf, nbyte);
+ err = errno;
+ (void) fprintf(stderr, "exiting write with %ld", (long) ret);
+ if (ret == (ssize_t) -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_fcntl (int fildes, int cmd, ...)
+{
+ va_list va;
+ int ret, err, iarg;
+ struct flock *farg;
+
+ va_start(va, cmd);
+ (void) fprintf(stderr, "entering fcntl(%d, %s", fildes, cmd_string(cmd));
+ if (cmd == F_DUPFD || cmd == F_SETFD || cmd == F_SETFL)
+ {
+ iarg = va_arg(va, int);
+ (void) fprintf(stderr, ", %d", iarg);
+ }
+ else if (cmd == F_GETLK || cmd == F_SETLK || cmd == F_SETLKW)
+ {
+ farg = va_arg(va, struct flock *);
+ (void) fprintf(stderr, ", %p", farg);
+ }
+ va_end(va);
+ (void) fprintf(stderr, ")\n");
+ (void) fflush(stderr);
+ if (cmd == F_DUPFD || cmd == F_SETFD || cmd == F_SETFL)
+ ret = fcntl(fildes, cmd, iarg);
+ else if (cmd == F_GETLK || cmd == F_SETLK || cmd == F_SETLKW)
+ ret = fcntl(fildes, cmd, farg);
+ else
+ ret = fcntl(fildes, cmd);
+ err = errno;
+ (void) fprintf(stderr, "exiting fcntl with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+off_t posix_debug_lseek (int fildes, off_t offset, int whence)
+{
+ off_t ret;
+ int err;
+
+ (void) fprintf(stderr, "entering lseek(%d, %ld, %s)\n", fildes, (long) offset, whence_string(whence));
+ (void) fflush(stderr);
+ ret = lseek(fildes, offset, whence);
+ err = errno;
+ (void) fprintf(stderr, "exiting lseek with %ld", (long) ret);
+ if (ret == (off_t) -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+/* Section 7: Device- and Class-Specific Functions */
+
+speed_t posix_debug_cfgetospeed (const struct termios *termios_p)
+{
+ speed_t ret;
+ int err, saved_errno;
+
+ (void) fprintf(stderr, "entering cfgetospeed(%p)\n", termios_p);
+ (void) fflush(stderr);
+ saved_errno = errno;
+ ret = cfgetospeed(termios_p);
+ err = errno;
+ (void) fprintf(stderr, "exiting cfgetospeed with %s", speed_string(ret));
+ if (saved_errno != err)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_cfsetospeed (struct termios *termios_p, speed_t speed)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering cfsetospeed(%p, %s)\n", termios_p, speed_string(speed));
+ (void) fflush(stderr);
+ ret = cfsetospeed(termios_p, speed);
+ err = errno;
+ (void) fprintf(stderr, "exiting cfsetospeed with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+speed_t posix_debug_cfgetispeed (const struct termios *termios_p)
+{
+ speed_t ret;
+ int err, saved_errno;
+
+ (void) fprintf(stderr, "entering cfgetispeed(%p)\n", termios_p);
+ (void) fflush(stderr);
+ saved_errno = errno;
+ ret = cfgetispeed(termios_p);
+ err = errno;
+ (void) fprintf(stderr, "exiting cfgetispeed with %s", speed_string(ret));
+ if (saved_errno != err)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_cfsetispeed (struct termios *termios_p, speed_t speed)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering cfsetispeed(%p, %s)\n", termios_p, speed_string(speed));
+ (void) fflush(stderr);
+ ret = cfsetispeed(termios_p, speed);
+ err = errno;
+ (void) fprintf(stderr, "exiting cfsetispeed with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_tcgetattr (int fildes, struct termios *termios_p)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering tcgetattr(%d, %p)\n", fildes, termios_p);
+ (void) fflush(stderr);
+ ret = tcgetattr(fildes, termios_p);
+ err = errno;
+ (void) fprintf(stderr, "exiting tcgetattr with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_tcsetattr (int fildes, int optional_actions, const struct termios *termios_p)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering tcsetattr(%d, %s, %p)\n", fildes, optact_string(optional_actions), termios_p);
+ (void) fflush(stderr);
+ ret = tcsetattr(fildes, optional_actions, termios_p);
+ err = errno;
+ (void) fprintf(stderr, "exiting tcsetattr with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_tcsendbreak (int fildes, int duration)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering tcsendbreak(%d, %d)\n", fildes, duration);
+ (void) fflush(stderr);
+ ret = tcsendbreak(fildes, duration);
+ err = errno;
+ (void) fprintf(stderr, "exiting tcsendbreak with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_tcdrain (int fildes)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering tcdrain(%d)\n", fildes);
+ (void) fflush(stderr);
+ ret = tcdrain(fildes);
+ err = errno;
+ (void) fprintf(stderr, "exiting tcdrain with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_tcflush (int fildes, int queue_selector)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering tcflush(%d, %s)\n", fildes, queue_string(queue_selector));
+ (void) fflush(stderr);
+ ret = tcflush(fildes, queue_selector);
+ err = errno;
+ (void) fprintf(stderr, "exiting tcflush with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_tcflow (int fildes, int action)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering tcflow(%d, %s)\n", fildes, action_string(action));
+ (void) fflush(stderr);
+ ret = tcflow(fildes, action);
+ err = errno;
+ (void) fprintf(stderr, "exiting tcflow with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+pid_t posix_debug_tcgetpgrp (int fildes)
+{
+ pid_t ret;
+ int err;
+
+ (void) fprintf(stderr, "entering tcgetpgrp(%d)\n", fildes);
+ (void) fflush(stderr);
+ ret = tcgetpgrp(fildes);
+ err = errno;
+ (void) fprintf(stderr, "exiting tcgetpgrp with %ld", (long) ret);
+ if (ret == (pid_t) -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+int posix_debug_tcsetpgrp (int fildes, pid_t pgrp_id)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering tcsetpgrp(%d, %ld)\n", fildes, (long) pgrp_id);
+ (void) fflush(stderr);
+ ret = tcsetpgrp(fildes, pgrp_id);
+ err = errno;
+ (void) fprintf(stderr, "exiting tcsetpgrp with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+/* Section 8.2: C Language Input/Output Functions */
+
+int posix_debug_fileno (FILE *stream)
+{
+ int ret, err;
+
+ (void) fprintf(stderr, "entering fileno(%p)\n", stream);
+ (void) fflush(stderr);
+ ret = fileno(stream);
+ err = errno;
+ (void) fprintf(stderr, "exiting fileno with %d", ret);
+ if (ret == -1)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+FILE *posix_debug_fdopen (int fildes, const char *type)
+{
+ FILE *ret;
+ int err;
+
+ (void) fprintf(stderr, "entering fdopen(%d, %s)\n", fildes, canonical_string(type));
+ (void) fflush(stderr);
+ ret = fdopen(fildes, type);
+ err = errno;
+ (void) fprintf(stderr, "exiting fdopen with %p", ret);
+ if (ret == NULL)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+/* Section 8.3: Other C Language Functions */
+
+void posix_debug_siglongjmp (sigjmp_buf env, int val)
+{
+ int err, saved_errno;
+
+ (void) fprintf(stderr, "entering siglongjmp(%p, %d)\n", env, val);
+ (void) fflush(stderr);
+ saved_errno = errno;
+ siglongjmp(env, val);
+ err = errno;
+ (void) fprintf(stderr, "exiting siglongjmp");
+ if (saved_errno != err)
+ (void) fprintf(stderr, "; errno: %d\n", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+}
+
+void posix_debug_tzset (void)
+{
+ int err, saved_errno;
+
+ (void) fprintf(stderr, "entering tzset()\n");
+ (void) fflush(stderr);
+ saved_errno = errno;
+ tzset();
+ err = errno;
+ (void) fprintf(stderr, "exiting tzset");
+ if (saved_errno != err)
+ (void) fprintf(stderr, "; errno: %d\n", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+}
+
+/* Section 9: System Databases */
+
+struct group *posix_debug_getgrgid (gid_t gid)
+{
+ struct group *ret;
+ int err;
+
+ (void) fprintf(stderr, "entering getgrgid(%lu)\n", (unsigned long) gid);
+ (void) fflush(stderr);
+ ret = getgrgid(gid);
+ err = errno;
+ (void) fprintf(stderr, "exiting getgrgid with %p", ret);
+ if (ret == NULL)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+struct group *posix_debug_getgrnam (const char *name)
+{
+ struct group *ret;
+ int err;
+
+ (void) fprintf(stderr, "entering getgrnam(%s)\n", canonical_string(name));
+ (void) fflush(stderr);
+ ret = getgrnam(name);
+ err = errno;
+ (void) fprintf(stderr, "exiting getgrnam with %p", ret);
+ if (ret == NULL)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+struct passwd *posix_debug_getpwuid (uid_t uid)
+{
+ struct passwd *ret;
+ int err;
+
+ (void) fprintf(stderr, "entering getpwuid(%lu)\n", (unsigned long) uid);
+ (void) fflush(stderr);
+ ret = getpwuid(uid);
+ err = errno;
+ (void) fprintf(stderr, "exiting getpwuid with %p", ret);
+ if (ret == NULL)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
+
+struct passwd *posix_debug_getpwnam (const char *name)
+{
+ struct passwd *ret;
+ int err;
+
+ (void) fprintf(stderr, "entering getpwnam(%s)\n", canonical_string(name));
+ (void) fflush(stderr);
+ ret = getpwnam(name);
+ err = errno;
+ (void) fprintf(stderr, "exiting getpwnam with %p", ret);
+ if (ret == NULL)
+ (void) fprintf(stderr, "; errno: %d", err);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ return ret;
+}
diff --git a/private/posix/programs/bsdlib/putenv.c b/private/posix/programs/bsdlib/putenv.c
new file mode 100644
index 000000000..226b1d77b
--- /dev/null
+++ b/private/posix/programs/bsdlib/putenv.c
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 1988 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#ifdef DF_POSIX
+#include <bsdlib.h>
+#endif
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)putenv.c 5.4 (Berkeley) 2/23/91";
+#endif /* LIBC_SCCS and not lint */
+
+#include <stdlib.h>
+#include <string.h>
+
+extern char *strdup();
+extern int setenv();
+
+int
+putenv(str)
+ const char *str;
+{
+ register char *p, *equal;
+ int rval;
+
+ if (!(p = strdup(str)))
+ return(1);
+ if (!(equal = index(p, '='))) {
+ (void)free(p);
+ return(1);
+ }
+ *equal = '\0';
+ rval = setenv(p, equal + 1, 1);
+ (void)free(p);
+ return(rval);
+}
diff --git a/private/posix/programs/bsdlib/pwcache.c b/private/posix/programs/bsdlib/pwcache.c
new file mode 100644
index 000000000..515f858f4
--- /dev/null
+++ b/private/posix/programs/bsdlib/pwcache.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)pwcache.c 5.4 (Berkeley) 6/1/90";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <utmp.h>
+#include <pwd.h>
+#include <grp.h>
+#include <stdio.h>
+#include <string.h>
+
+#define NCACHE 64 /* power of 2 */
+#define MASK NCACHE - 1 /* bits to store with */
+
+static int pwopen = 0;
+static int gropen = 0;
+
+char *
+user_from_uid(uid, nouser)
+ uid_t uid;
+ int nouser;
+{
+ static struct ncache {
+ uid_t uid;
+ char name[UT_NAMESIZE + 1];
+ } c_uid[NCACHE];
+ static char nbuf[15]; /* 32 bits == 10 digits */
+ register struct passwd *pw;
+ register struct ncache *cp;
+
+ cp = c_uid + (uid & MASK);
+ if (cp->uid != uid || !*cp->name) {
+ if (pwopen == 0) {
+#ifndef _POSIX_SOURCE //MSS
+ setpassent(1);
+#endif
+ pwopen++;
+ }
+ if (!(pw = getpwuid(uid))) {
+ if (nouser)
+ return((char *)NULL);
+ (void)sprintf(nbuf, "%u", uid);
+ return(nbuf);
+ }
+ cp->uid = uid;
+ (void)strncpy(cp->name, pw->pw_name, UT_NAMESIZE);
+ cp->name[UT_NAMESIZE] = '\0';
+ }
+ return(cp->name);
+}
+
+char *
+group_from_gid(gid, nogroup)
+ gid_t gid;
+ int nogroup;
+{
+ static struct ncache {
+ gid_t gid;
+ char name[UT_NAMESIZE];
+ } c_gid[NCACHE];
+ static char nbuf[15]; /* 32 bits == 10 digits */
+ register struct group *gr;
+ register struct ncache *cp;
+
+ cp = c_gid + (gid & MASK);
+ if (cp->gid != gid || !*cp->name) {
+ if (gropen == 0) {
+#ifndef _POSIX_SOURCE
+ setgroupent(1);
+#endif
+ gropen++;
+ }
+ if (!(gr = getgrgid(gid))) {
+ if (nogroup)
+ return((char *)NULL);
+ (void)sprintf(nbuf, "%u", gid);
+ return(nbuf);
+ }
+ cp->gid = gid;
+ (void)strncpy(cp->name, gr->gr_name, UT_NAMESIZE);
+ cp->name[UT_NAMESIZE] = '\0';
+ }
+ return(cp->name);
+}
diff --git a/private/posix/programs/bsdlib/rindex.c b/private/posix/programs/bsdlib/rindex.c
new file mode 100644
index 000000000..aabd2dc9a
--- /dev/null
+++ b/private/posix/programs/bsdlib/rindex.c
@@ -0,0 +1,10 @@
+#include <string.h>
+/*
+ * Rindex: Posix implementation DF_MSS
+ */
+
+
+char *rindex(const char *p, int ch)
+{
+ return(strrchr(p, ch));
+}
diff --git a/private/posix/programs/bsdlib/scandir.c b/private/posix/programs/bsdlib/scandir.c
new file mode 100644
index 000000000..1cd714292
--- /dev/null
+++ b/private/posix/programs/bsdlib/scandir.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#ifdef DF_POSIX //MSS prototype for __P not in included .h's
+#include <sys/cdefs.h>
+#endif
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)scandir.c 5.10 (Berkeley) 2/23/91";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * Scan the directory dirname calling select to make a list of selected
+ * directory entries then sort using qsort and compare routine dcomp.
+ * Returns the number of entries and a pointer to a list of pointers to
+ * struct dirent (through namelist). Returns -1 if there were any errors.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <string.h>
+
+extern void bcopy();
+
+/*
+ * The DIRSIZ macro gives the minimum record length which will hold
+ * the directory entry. This requires the amount of space in struct dirent
+ * without the d_name field, plus enough space for the name with a terminating
+ * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary.
+ */
+#undef DIRSIZ
+#define DIRSIZ(dp) \
+ ((sizeof (struct dirent) - (_POSIX_NAME_MAX+1)) + ((strlen((dp)->d_name)+1 + 3) &~ 3))
+
+int
+scandir(dirname, namelist, select, dcomp)
+ const char *dirname;
+ struct dirent ***namelist;
+ int (*select) __P((struct dirent *));
+ int (*dcomp) __P((const void *, const void *));
+{
+ register struct dirent *d, *p, **names;
+ register size_t nitems;
+ struct stat stb;
+ long arraysz;
+ DIR *dirp;
+
+ if ((dirp = opendir(dirname)) == NULL)
+ return(-1);
+#ifndef _POSIX_SOURCE
+ if (fstat(dirp->dd_fd, &stb) < 0)
+ return(-1);
+#else
+ stb.st_size = 1024;
+#endif
+ /*
+ * estimate the array size by taking the size of the directory file
+ * and dividing it by a multiple of the minimum size entry.
+ */
+ arraysz = (stb.st_size / 24);
+
+ names = (struct dirent **)malloc(arraysz * sizeof(struct dirent *));
+ if (names == NULL)
+ return(-1);
+
+ nitems = 0;
+ while ((d = readdir(dirp)) != NULL) {
+ if (select != NULL && !(*select)(d))
+ continue; /* just selected names */
+ /*
+ * Make a minimum size copy of the data
+ */
+ p = (struct dirent *)malloc(DIRSIZ(d));
+ if (p == NULL)
+ return(-1);
+#ifndef _POSIX_SOURCE
+ p->d_ino = d->d_ino;
+ p->d_reclen = d->d_reclen;
+#endif
+#ifdef _POSIX_SOURCE
+
+ bcopy(d->d_name, p->d_name, strlen(d->d_name) + 1);
+#else
+ p->d_namlen = d->d_namlen;
+ bcopy(d->d_name, p->d_name, p->d_namlen + 1);
+#endif
+ /*
+ * Check to make sure the array has space left and
+ * realloc the maximum size.
+ */
+ if (++nitems >= (unsigned long)arraysz) {
+#ifndef _POSIX_SOURCE
+ if (fstat(dirp->dd_fd, &stb) < 0)
+ return(-1); /* just might have grown */
+#endif
+ arraysz = stb.st_size / 12;
+ names = (struct dirent **)realloc((char *)names,
+ arraysz * sizeof(struct dirent *));
+ if (names == NULL)
+ return(-1);
+ }
+ names[nitems-1] = p;
+ }
+ closedir(dirp);
+ if (nitems && dcomp != NULL)
+ qsort(names, nitems, sizeof(struct dirent *), dcomp);
+ *namelist = names;
+ return(nitems);
+}
+
+/*
+ * Alphabetic order comparison routine for those who want it.
+ */
+int
+alphasort(d1, d2)
+ const void *d1;
+ const void *d2;
+{
+ return(strcmp((*(struct dirent **)d1)->d_name,
+ (*(struct dirent **)d2)->d_name));
+}
diff --git a/private/posix/programs/bsdlib/seekdir.c b/private/posix/programs/bsdlib/seekdir.c
new file mode 100644
index 000000000..8035a2b93
--- /dev/null
+++ b/private/posix/programs/bsdlib/seekdir.c
@@ -0,0 +1,12 @@
+#include <sys/types.h>
+#include <dirent.h>
+
+void seekdir(DIR *dirp, long loc)
+{
+ int i;
+
+ rewinddir(dirp);
+
+ for(i = 0; i < loc; i++)
+ readdir(dirp);
+}
diff --git a/private/posix/programs/bsdlib/setenv.c b/private/posix/programs/bsdlib/setenv.c
new file mode 100644
index 000000000..72f3060a2
--- /dev/null
+++ b/private/posix/programs/bsdlib/setenv.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 1987 Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#ifdef DF_POSIX
+#include <bsdlib.h>
+#endif
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)setenv.c 5.6 (Berkeley) 6/4/91";
+#endif /* LIBC_SCCS and not lint */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * setenv --
+ * Set the value of the environmental variable "name" to be
+ * "value". If rewrite is set, replace any current value.
+ */
+setenv(name, value, rewrite)
+ register const char *name;
+ register const char *value;
+ int rewrite;
+{
+ extern char **environ;
+ static int alloced; /* if allocated space before */
+ register char *C;
+ int l_value, offset;
+ char *_findenv();
+
+ if (*value == '=') /* no `=' in value */
+ ++value;
+ l_value = strlen(value);
+ if ((C = _findenv(name, &offset))) { /* find if already exists */
+ if (!rewrite)
+ return (0);
+ if (strlen(C) >= (unsigned)l_value) { /* old larger; copy over */
+ while (*C++ = *value++);
+ return (0);
+ }
+ } else { /* create new slot */
+ register int cnt;
+ register char **P;
+
+ for (P = environ, cnt = 0; *P; ++P, ++cnt);
+ if (alloced) { /* just increase size */
+ environ = (char **)realloc((char *)environ,
+ (size_t)(sizeof(char *) * (cnt + 2)));
+ if (!environ)
+ return (-1);
+ }
+ else { /* get new space */
+ alloced = 1; /* copy old entries into it */
+ P = (char **)malloc((size_t)(sizeof(char *) *
+ (cnt + 2)));
+ if (!P)
+ return (-1);
+ bcopy((void *)environ, (void *)P, cnt * sizeof(char *));
+ environ = P;
+ }
+ environ[cnt + 1] = NULL;
+ offset = cnt;
+ }
+ for (C = (char *)name; *C && *C != '='; ++C); /* no `=' in name */
+ if (!(environ[offset] = /* name + `=' + value */
+ malloc((size_t)((int)(C - name) + l_value + 2))))
+ return (-1);
+ for (C = environ[offset]; (*C = *name++) && *C != '='; ++C)
+ ;
+ for (*C++ = '='; *C++ = *value++; )
+ ;
+ return (0);
+}
+
+/*
+ * unsetenv(name) --
+ * Delete environmental variable "name".
+ */
+void
+unsetenv(name)
+ const char *name;
+{
+ extern char **environ;
+ register char **P;
+ int offset;
+
+ while (_findenv(name, &offset)) /* if set multiple times */
+ for (P = &environ[offset];; ++P)
+ if (!(*P = *(P + 1)))
+ break;
+}
diff --git a/private/posix/programs/bsdlib/setmode.c b/private/posix/programs/bsdlib/setmode.c
new file mode 100644
index 000000000..fda6ed6e7
--- /dev/null
+++ b/private/posix/programs/bsdlib/setmode.c
@@ -0,0 +1,461 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)setmode.c 5.6 (Berkeley) 5/27/91";
+#endif /* LIBC_SCCS and not lint */
+
+#ifdef _POSIX_SOURCE
+#include <misc.h>
+#endif
+
+#ifdef _POSIX_SOURCE //DF_DSC POSIX does not need this
+#else // only MAXPATHLEN was found there
+ #include <sys/param.h> // and it wants machine directory stuff
+#endif
+
+#include <sys/stat.h>
+#include <sys/errno.h>
+#ifdef SETMODE_DEBUG
+#include <stdio.h>
+#endif
+#include <stdlib.h>
+#include <ctype.h>
+
+#define SET_LEN 6 /* initial # of bitcmd struct to malloc */
+#define SET_LEN_INCR 4 /* # of bitcmd structs to add as needed */
+
+struct bitcmd {
+ char cmd;
+ char cmd2;
+ mode_t bits;
+};
+
+#define CMD2_CLR 0x01
+#define CMD2_SET 0x02
+#define CMD2_GBITS 0x04
+#define CMD2_OBITS 0x08
+#define CMD2_UBITS 0x10
+
+/*
+ * Given the old mode and an array of bitcmd structures, apply the operations
+ * described in the bitcmd structures to the old mode, and return the new mode.
+ * Note that there is no '=' command; a strict assignment is just a '-' (clear
+ * bits) followed by a '+' (set bits).
+ */
+mode_t
+getmode(bbox, omode)
+ void *bbox;
+ mode_t omode;
+{
+ register struct bitcmd *set;
+ register mode_t newmode, value;
+
+ set = (struct bitcmd *)bbox;
+ newmode = omode;
+ for (value = 0;; set++)
+ switch(set->cmd) {
+ /*
+ * When copying the user, group or other bits around, we "know"
+ * where the bit are in the mode so that we can do shifts to
+ * copy them around. If we don't use shifts, it gets real
+ * grundgy with lots of single bit checks and bit sets.
+ */
+ case 'u':
+ value = (newmode & S_IRWXU) >> 6;
+ goto common;
+
+ case 'g':
+ value = (newmode & S_IRWXG) >> 3;
+ goto common;
+
+ case 'o':
+ value = newmode & S_IRWXO;
+ common:
+ if (set->cmd2 & CMD2_CLR) {
+ if (set->cmd2 & CMD2_UBITS)
+ newmode &= ~(S_IRWXU & set->bits);
+ if (set->cmd2 & CMD2_GBITS)
+ newmode &= ~(S_IRWXG & set->bits);
+ if (set->cmd2 & CMD2_OBITS)
+ newmode &= ~(S_IRWXO & set->bits);
+ }
+ if (set->cmd2 & CMD2_SET) {
+ if (set->cmd2 & CMD2_UBITS)
+ newmode |= (value<<6) & set->bits;
+ if (set->cmd2 & CMD2_GBITS)
+ newmode |= (value<<3) & set->bits;
+ if (set->cmd2 & CMD2_OBITS)
+ newmode |= value & set->bits;
+ }
+ break;
+
+ case '+':
+ newmode |= set->bits;
+ break;
+
+ case '-':
+ newmode &= ~set->bits;
+ break;
+
+ case 'X':
+ if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH))
+ newmode |= set->bits;
+ break;
+
+ case '\0':
+ default:
+#ifdef SETMODE_DEBUG
+ (void)printf("getmode(, %04o) -> %04o\n",
+ omode, newmode);
+#endif
+ return(newmode);
+ }
+}
+
+#define STANDARD_BITS (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
+
+static struct bitcmd *
+addcmd(set, op, who, oparg, mask)
+ struct bitcmd *set;
+ register int oparg, who;
+ register int op;
+ mode_t mask;
+{
+ switch (op) {
+ case '+':
+ case 'X':
+ set->cmd = op;
+ set->bits = (who ? who : mask) & oparg;
+ break;
+
+ case '-':
+ set->cmd = '-';
+ set->bits = (who ? who : (S_IRWXU|S_IRWXG|S_IRWXO)) & oparg;
+ break;
+
+ case '=':
+ set->cmd = '-';
+ if (!who) {
+ set->bits = STANDARD_BITS;
+ who = mask;
+ } else
+ set->bits = who;
+ set++;
+
+ set->cmd = '+';
+ set->bits = who & oparg;
+ break;
+ case 'u':
+ case 'g':
+ case 'o':
+ set->cmd = op;
+ if (who) {
+ set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) |
+ ((who & S_IRGRP) ? CMD2_GBITS : 0) |
+ ((who & S_IROTH) ? CMD2_OBITS : 0);
+ set->bits = (unsigned long)~0;
+ } else {
+ set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS;
+ set->bits = mask;
+ }
+
+ if (oparg == '+')
+ set->cmd2 |= CMD2_SET;
+ else if (oparg == '-')
+ set->cmd2 |= CMD2_CLR;
+ else if (oparg == '=')
+ set->cmd2 |= CMD2_SET|CMD2_CLR;
+ break;
+ }
+ return(set+1);
+}
+
+#define ADDCMD(a, b, c, d) \
+ if (set >= endset) { \
+ register struct bitcmd *newset; \
+ setlen += SET_LEN_INCR; \
+ newset = realloc(saveset, sizeof(struct bitcmd) * setlen); \
+ if (!saveset) \
+ return(NULL); \
+ set = newset + (set - saveset); \
+ saveset = newset; \
+ endset = newset + (setlen - 2); \
+ } \
+ set = addcmd(set, (a), (b), (c), (d))
+
+void *
+setmode(p)
+ register char *p;
+{
+ register int perm, who;
+ register char op;
+ mode_t mask;
+ struct bitcmd *set, *saveset, *endset;
+ int permXbits, setlen;
+ static void compress_mode();
+
+ /*
+ * Get a copy of the mask for the permissions that are mask relative.
+ * Flip the bits, we want what's not set.
+ */
+ (void)umask(mask = umask(0));
+ mask = ~mask;
+
+ setlen = SET_LEN + 2;
+
+ set = (struct bitcmd *)malloc((u_int)(sizeof(struct bitcmd) * setlen));
+ if (!set)
+ return(NULL);
+ saveset = set;
+ endset = set + (setlen - 2);
+
+ /*
+ * If an absolute number, get it and return; disallow non-octal digits
+ * or illegal bits.
+ */
+ if (isdigit(*p)) {
+ perm = (mode_t)strtol(p, (char **)0, 8);
+#ifdef _POSIX_SOURCE
+ if (perm & ~(STANDARD_BITS)) {
+#else
+ if (perm & ~(STANDARD_BITS|S_ISTXT)) {
+#endif
+ free(saveset);
+ return(NULL);
+ }
+ while (*++p)
+ if (*p < '0' || *p > '7') {
+ free(saveset);
+ return(NULL);
+ }
+#ifdef _POSIX_SOURCE
+ ADDCMD('=', (STANDARD_BITS), perm, mask);
+#else
+ ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask);
+#endif
+ return((void *)saveset);
+ }
+
+ if (!*p) {
+ free(saveset);
+ return(NULL);
+ }
+ /*
+ * Build list of structures to set/clear/copy bits as described by
+ * each clause of the symbolic mode.
+ */
+ for (;;) {
+ /* First, find out which bits might be modified. */
+ for (who = 0;; ++p) {
+ switch (*p) {
+ case 'a':
+ who |= STANDARD_BITS;
+ break;
+ case 'u':
+ who |= S_ISUID|S_IRWXU;
+ break;
+ case 'g':
+ who |= S_ISGID|S_IRWXG;
+ break;
+ case 'o':
+ who |= S_IRWXO;
+ break;
+ default:
+ goto getop;
+ }
+ }
+ getop:
+
+ if ((op = *p++) != '+' && op != '-' && op != '=') {
+ free(saveset);
+ return(NULL);
+ }
+
+#ifndef _POSIX_SOURCE
+ who &= ~S_ISTXT;
+#endif
+ for (perm = 0, permXbits = 0;; ++p) {
+ switch (*p) {
+ case 'r':
+ perm |= S_IRUSR|S_IRGRP|S_IROTH;
+ break;
+ case 's':
+ /* If only "other" bits ignore set-id. */
+ if (who & ~S_IRWXO)
+ perm |= S_ISUID|S_ISGID;
+ break;
+ case 't':
+ /* If only "other" bits ignore sticky. */
+ if (who & ~S_IRWXO) {
+#ifndef _POSIX_SOURCE
+ who |= S_ISTXT;
+ perm |= S_ISTXT;
+#endif
+ }
+ break;
+ case 'w':
+ perm |= S_IWUSR|S_IWGRP|S_IWOTH;
+ break;
+ case 'X':
+ permXbits = S_IXUSR|S_IXGRP|S_IXOTH;
+ break;
+ case 'x':
+ perm |= S_IXUSR|S_IXGRP|S_IXOTH;
+ break;
+ case 'u':
+ case 'g':
+ case 'o':
+ /*
+ * When ever we hit 'u', 'g', or 'o', we have
+ * to flush out any partial mode that we have,
+ * and then do the copying of the mode bits.
+ */
+ if (perm) {
+ ADDCMD(op, who, perm, mask);
+ perm = 0;
+ }
+ if (op == '+' && permXbits) {
+ ADDCMD('X', who, permXbits, mask);
+ permXbits = 0;
+ }
+ ADDCMD(*p, who, op, mask);
+ break;
+
+ default:
+ /*
+ * Add any permissions that we haven't already
+ * done.
+ */
+ if (perm) {
+ ADDCMD(op, who, perm, mask);
+ perm = 0;
+ }
+ if (permXbits) {
+ ADDCMD('X', who, permXbits, mask);
+ permXbits = 0;
+ }
+ goto apply;
+ }
+ }
+
+apply: if (!*p)
+ break;
+ if (*p != ',')
+ goto getop;
+ ++p;
+ }
+ set->cmd = 0;
+#ifdef SETMODE_DEBUG
+ (void)printf("Before compress_mode()\n");
+ dumpmode(saveset);
+#endif
+ compress_mode(saveset);
+#ifdef SETMODE_DEBUG
+ (void)printf("After compress_mode()\n");
+ dumpmode(saveset);
+#endif
+ return((void *)saveset);
+}
+
+#ifdef SETMODE_DEBUG
+dumpmode(set)
+ register struct bitcmd *set;
+{
+ for (; set->cmd; ++set)
+ (void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n",
+ set->cmd, set->bits, set->cmd2 ? " cmd2:" : "",
+ set->cmd2 & CMD2_CLR ? " CLR" : "",
+ set->cmd2 & CMD2_SET ? " SET" : "",
+ set->cmd2 & CMD2_UBITS ? " UBITS" : "",
+ set->cmd2 & CMD2_GBITS ? " GBITS" : "",
+ set->cmd2 & CMD2_OBITS ? " OBITS" : "");
+}
+#endif
+
+/*
+ * Given an array of bitcmd structures, compress by compacting consecutive
+ * '+', '-' and 'X' commands into at most 3 commands, one of each. The 'u',
+ * 'g' and 'o' commands continue to be separate. They could probably be
+ * compacted, but it's not worth the effort.
+ */
+static
+void
+compress_mode(set)
+ register struct bitcmd *set;
+{
+ register struct bitcmd *nset;
+ register int setbits, clrbits, Xbits, op;
+
+ for (nset = set;;) {
+ /* Copy over any 'u', 'g' and 'o' commands. */
+ while ((op = nset->cmd) != '+' && op != '-' && op != 'X') {
+ *set++ = *nset++;
+ if (!op)
+ return;
+ }
+
+ for (setbits = clrbits = Xbits = 0;; nset++) {
+ if ((op = nset->cmd) == '-') {
+ clrbits |= nset->bits;
+ setbits &= ~nset->bits;
+ Xbits &= ~nset->bits;
+ } else if (op == '+') {
+ setbits |= nset->bits;
+ clrbits &= ~nset->bits;
+ Xbits &= ~nset->bits;
+ } else if (op == 'X')
+ Xbits |= nset->bits & ~setbits;
+ else
+ break;
+ }
+ if (clrbits) {
+ set->cmd = '-';
+ set->cmd2 = 0;
+ set->bits = clrbits;
+ set++;
+ }
+ if (setbits) {
+ set->cmd = '+';
+ set->cmd2 = 0;
+ set->bits = setbits;
+ set++;
+ }
+ if (Xbits) {
+ set->cmd = 'X';
+ set->cmd2 = 0;
+ set->bits = Xbits;
+ set++;
+ }
+ }
+}
diff --git a/private/posix/programs/bsdlib/snprintf.c b/private/posix/programs/bsdlib/snprintf.c
new file mode 100644
index 000000000..52396544a
--- /dev/null
+++ b/private/posix/programs/bsdlib/snprintf.c
@@ -0,0 +1,81 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)snprintf.c 5.1 (Berkeley) 1/20/91";
+#endif /* LIBC_SCCS and not lint */
+
+#include <stdio.h>
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+int
+#ifdef __STDC__
+snprintf(char *str, size_t n, const char *fmt, ...)
+#else
+snprintf(str, n, fmt, va_alist)
+ char *str;
+ size_t n;
+ char *fmt;
+ va_dcl
+#endif
+{
+ int ret;
+ va_list ap;
+ FILE f;
+
+ if ((int)n < 1)
+ return (EOF);
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+#ifndef _POSIX_SOURCE
+ f._flags = __SWR | __SSTR;
+ f._bf._base = f._p = (unsigned char *)str;
+ f._bf._size = f._w = n - 1;
+#endif
+ ret = vfprintf(&f, fmt, ap);
+#ifndef _POSIX_SOURCE
+ *f._p = 0;
+#endif
+ va_end(ap);
+ return (ret);
+}
diff --git a/private/posix/programs/bsdlib/sources b/private/posix/programs/bsdlib/sources
new file mode 100644
index 000000000..abddd6e13
--- /dev/null
+++ b/private/posix/programs/bsdlib/sources
@@ -0,0 +1,43 @@
+WIMPYMASM=1
+
+MAJORCOMP=posix
+MINORCOMP=client
+
+TARGETNAME=bsdpsx
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+INCLUDES=\nt\public\sdk\inc\posix;..\inc;..\inc\bsd;..\inc\df
+
+SOURCES=bcopy.c \
+ bzero.c \
+ for2bak.c \
+ fts.c \
+ getenv.c \
+ getopt.c \
+ getwd.c \
+ index.c \
+ lstat.c \
+ mknod.c \
+ putenv.c \
+ pwcache.c \
+ rindex.c \
+ scandir.c \
+ seekdir.c \
+ setenv.c \
+ setmode.c \
+ snprintf.c \
+ strmode.c \
+ stubs.c \
+ telldir.c \
+ utimes.c \
+ vfork.c
+
+C_DEFINES=-DSTDC_HEADERS -D_POSIX_SOURCE -DDIRENT -DSTACK_DIRECTION=-1 -DDF_POSIX -D_POSIX_ -D__STDC__
+UMTYPE=posix
+UMLIBS=
+OPTIONAL_UMTEST=
+UMBASE=0xa00000
+
+
+386_STDCALL=0
diff --git a/private/posix/programs/bsdlib/strmode.c b/private/posix/programs/bsdlib/strmode.c
new file mode 100644
index 000000000..62974d09a
--- /dev/null
+++ b/private/posix/programs/bsdlib/strmode.c
@@ -0,0 +1,164 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strmode.c 5.3 (Berkeley) 5/18/90";
+#endif /* LIBC_SCCS and not lint */
+
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+
+void
+strmode(mode, p)
+ register mode_t mode;
+ register char *p;
+{
+//#ifdef ENHANCE
+ /* print type */
+ switch (mode & S_IFMT)
+ {
+ case S_IFDIR: /* directory */
+ *p++ = 'd';
+ break;
+ case S_IFCHR: /* character special */
+ *p++ = 'c';
+ break;
+ case S_IFBLK: /* block special */
+ *p++ = 'b';
+ break;
+ case S_IFREG: /* regular */
+ *p++ = '-';
+ break;
+
+#ifndef _POSIX_SOURCE
+ case S_IFLNK: /* symbolic link */
+ *p++ = 'l';
+ break;
+ case S_IFSOCK: /* socket */
+ *p++ = 's';
+ break;
+#endif
+
+#ifdef S_IFIFO
+ case S_IFIFO: /* fifo */
+ *p++ = 'p';
+ break;
+#endif
+
+ default: /* unknown */
+ *p++ = '?';
+ break;
+ }
+//#endif
+ /* usr */
+ if (mode & S_IRUSR)
+ *p++ = 'r';
+ else
+ *p++ = '-';
+ if (mode & S_IWUSR)
+ *p++ = 'w';
+ else
+ *p++ = '-';
+ switch (mode & (S_IXUSR | S_ISUID)) {
+ case 0:
+ *p++ = '-';
+ break;
+ case S_IXUSR:
+ *p++ = 'x';
+ break;
+ case S_ISUID:
+ *p++ = 'S';
+ break;
+ case S_IXUSR | S_ISUID:
+ *p++ = 's';
+ break;
+ }
+ /* group */
+ if (mode & S_IRGRP)
+ *p++ = 'r';
+ else
+ *p++ = '-';
+ if (mode & S_IWGRP)
+ *p++ = 'w';
+ else
+ *p++ = '-';
+ switch (mode & (S_IXGRP | S_ISGID)) {
+ case 0:
+ *p++ = '-';
+ break;
+ case S_IXGRP:
+ *p++ = 'x';
+ break;
+ case S_ISGID:
+ *p++ = 'S';
+ break;
+ case S_IXGRP | S_ISGID:
+ *p++ = 's';
+ break;
+ }
+ /* other */
+ if (mode & S_IROTH)
+ *p++ = 'r';
+ else
+ *p++ = '-';
+ if (mode & S_IWOTH)
+ *p++ = 'w';
+ else
+ *p++ = '-';
+//#ifdef ENHANCE
+#ifdef _POSIX_SOURCE
+ switch (mode & (S_IXOTH)) {
+#else
+ switch (mode & (S_IXOTH | S_ISVTX)) {
+#endif
+ case 0:
+ *p++ = '-';
+ break;
+ case S_IXOTH:
+ *p++ = 'x';
+ break;
+#ifndef _POSIX_SOURCE
+ case S_ISVTX:
+ *p++ = 'T';
+ break;
+ case S_IXOTH | S_ISVTX:
+ *p++ = 't';
+ break;
+#endif
+ }
+//#endif
+ *p++ = ' '; /* will be a '+' if ACL's implemented */
+ *p = '\0';
+}
diff --git a/private/posix/programs/bsdlib/stubs.c b/private/posix/programs/bsdlib/stubs.c
new file mode 100644
index 000000000..e3b039bd9
--- /dev/null
+++ b/private/posix/programs/bsdlib/stubs.c
@@ -0,0 +1,51 @@
+#include <pwd.h>
+#include <grp.h>
+#include <stdio.h>
+
+#if 0
+
+struct passwd *getpwuid(uid_t uid)
+{
+ struct passwd *p;
+ char *name, *dir, *shell;
+
+ p = malloc(sizeof(struct passwd));
+
+ name = malloc(80);
+ dir = malloc(80);
+ shell = malloc(80);
+
+ //strcpy(name, "pw_name");
+ strcpy(name, "informix");
+ strcpy(dir, "/");
+ strcpy(shell, "no_shell");
+
+ p->pw_name = name;
+ p->pw_uid = uid;
+ p->pw_gid = 0x110000;
+ p->pw_dir = dir;
+ p->pw_shell = shell;
+
+ return p;
+}
+
+
+struct group *getgrgid(gid_t gid)
+{
+ struct group *p;
+ char *name, *members = NULL;
+
+ p = malloc(sizeof(struct group));
+
+ name = malloc(80);
+
+// strcpy(name, "gr_name");
+ strcpy(name, "informix");
+
+ p->gr_name = name;
+ p->gr_gid = gid;
+ p->gr_mem = members;
+
+ return p;
+}
+#endif
diff --git a/private/posix/programs/bsdlib/telldir.c b/private/posix/programs/bsdlib/telldir.c
new file mode 100644
index 000000000..832bd9245
--- /dev/null
+++ b/private/posix/programs/bsdlib/telldir.c
@@ -0,0 +1,10 @@
+#include <sys/types.h>
+#include <dirent.h>
+
+long telldir(DIR *dirp)
+{
+
+//puts("in telldir");
+
+ return(dirp->Index);
+}
diff --git a/private/posix/programs/bsdlib/utimes.c b/private/posix/programs/bsdlib/utimes.c
new file mode 100644
index 000000000..acef7656a
--- /dev/null
+++ b/private/posix/programs/bsdlib/utimes.c
@@ -0,0 +1,39 @@
+#if 0
+# include <stdio.h>
+#endif
+#include <sys/types.h>
+#include <time.h>
+#include <utime.h>
+#include <unistd.h>
+
+struct timeval {
+ long tv_sec; /* seconds */
+ long tv_usec; /* and microseconds */
+};
+
+int
+#if __STDC__
+utimes (const char *file, struct timeval *tvp)
+#else
+utimes (file, tvp)
+const char *file;
+struct timeval tvp [];
+#endif
+{
+#ifdef _POSIX_SOURCE
+ struct utimbuf ut;
+#else
+ struct utimebuf ut;
+#endif
+
+ if (tvp == NULL) {
+ ut.actime = ut.modtime = time(NULL);
+ } else {
+ ut.actime = tvp[0].tv_sec;
+ ut.modtime = tvp[1].tv_sec;
+ }
+#if 0
+ printf("time %ld %ld\n", ut.actime, ut.modtime);
+#endif
+ return utime(file, &ut);
+}
diff --git a/private/posix/programs/bsdlib/vfork.c b/private/posix/programs/bsdlib/vfork.c
new file mode 100644
index 000000000..c50cc4c83
--- /dev/null
+++ b/private/posix/programs/bsdlib/vfork.c
@@ -0,0 +1,6 @@
+extern int fork();
+
+int vfork()
+{
+ return (fork());
+}
diff --git a/private/posix/programs/dirs b/private/posix/programs/dirs
new file mode 100644
index 000000000..e5b3abd1e
--- /dev/null
+++ b/private/posix/programs/dirs
@@ -0,0 +1,25 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+DIRS=psxses bsdlib pax hello
+
+OPTIONAL_DIRS=psxarc
diff --git a/private/posix/programs/hello/makefile b/private/posix/programs/hello/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/posix/programs/hello/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/posix/programs/hello/psxhello.c b/private/posix/programs/hello/psxhello.c
new file mode 100644
index 000000000..edd05a66e
--- /dev/null
+++ b/private/posix/programs/hello/psxhello.c
@@ -0,0 +1,19 @@
+#include <stdio.h>
+
+extern int __argc;
+extern char **__argv;
+
+int
+main( int argc, char **argv )
+{
+ int i;
+
+ printf( "Hello world. I am a Posix application. My command line arguments were:\n" );
+ printf( "__argc: %08x __argv: %08x (%08x)\n", __argc, __argv, argv );
+ printf( "argc: %u\n", argc );
+ for (i=0; i<argc; i++) {
+ printf( "argv[ %02u ]: %s\n", i, argv[ i ] );
+ }
+
+ return 0;
+}
diff --git a/private/posix/programs/hello/sources b/private/posix/programs/hello/sources
new file mode 100644
index 000000000..70a13f536
--- /dev/null
+++ b/private/posix/programs/hello/sources
@@ -0,0 +1,17 @@
+MAJORCOMP=posix
+MINORCOMP=psxhello
+
+TARGETNAME=psxhello
+TARGETPATH=obj
+TARGETTYPE=UMAPPL_NOLIB
+
+INCLUDES=\nt\public\sdk\inc\posix
+
+386_STDCALL=0
+
+SOURCES=
+
+C_DEFINES=-DWIN_NT -DSTDC_HEADERS -D_POSIX_SOURCE -DDIRENT -DSTACK_DIRECTION=-1 -D_POSIX_
+UMTYPE=posix
+UMAPPL=psxhello
+UMBASE=0xa00000
diff --git a/private/posix/programs/inc/bsd/ar.h b/private/posix/programs/inc/bsd/ar.h
new file mode 100644
index 000000000..c12677ddd
--- /dev/null
+++ b/private/posix/programs/inc/bsd/ar.h
@@ -0,0 +1,62 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Hugh Smith at The University of Guelph.
+ *
+ * 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.
+ *
+ * @(#)ar.h 5.4 (Berkeley) 4/3/91
+ */
+
+#ifndef _AR_H_
+#define _AR_H_
+
+/* Pre-4BSD archives had these magic numbers in them. */
+#define OARMAG1 0177555
+#define OARMAG2 0177545
+
+#define ARMAG "!<arch>\n" /* ar "magic number" */
+#define SARMAG 8 /* strlen(ARMAG); */
+
+#define AR_EFMT1 "#1/" /* extended format #1 */
+
+struct ar_hdr {
+ char ar_name[16]; /* name */
+ char ar_date[12]; /* modification time */
+ char ar_uid[6]; /* user id */
+ char ar_gid[6]; /* group id */
+ char ar_mode[8]; /* octal file permissions */
+ char ar_size[10]; /* size in bytes */
+#define ARFMAG "`\n"
+ char ar_fmag[2]; /* consistency check */
+};
+
+#endif /* !_AR_H_ */
diff --git a/private/posix/programs/inc/bsd/fts.h b/private/posix/programs/inc/bsd/fts.h
new file mode 100644
index 000000000..596fc1430
--- /dev/null
+++ b/private/posix/programs/inc/bsd/fts.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * @(#)fts.h 5.14 (Berkeley) 4/3/91
+ */
+
+
+
+#ifndef _FTS_H_
+#define _FTS_H_
+
+typedef struct {
+ struct _ftsent *fts_cur; /* current node */
+ struct _ftsent *fts_child; /* linked list of children */
+ struct _ftsent *fts_savelink; /* saved link if node had a cycle */
+ struct _ftsent **fts_array; /* sort array */
+ dev_t rdev; /* starting device # */
+ char *fts_path; /* path for this descent */
+ int fts_dfd; /* fd for directories */
+#ifdef _POSIX_SOURCE /* DF_DSC: bacause no fchdir in POSIX */
+ char *fts_rpath; /* path to root of search */
+#else
+ int fts_rfd; /* fd for root */
+#endif
+ int fts_pathlen; /* sizeof(path) */
+ int fts_nitems; /* elements in the sort array */
+ int (*fts_compar)(); /* compare function */
+
+#define FTS_LOGICAL 0x001 /* logical walk */
+#define FTS_NOCHDIR 0x002 /* don't change directories */
+#define FTS_NOSTAT 0x004 /* don't get stat info */
+#define FTS_PHYSICAL 0x008 /* physical walk */
+#define FTS_SEEDOT 0x010 /* return dot and dot-dot */
+#define FTS_STOP 0x020 /* (private) unrecoverable error */
+#define FTS_XDEV 0x040 /* don't cross devices */
+ int fts_options; /* openfts() options */
+} FTS;
+
+typedef struct _ftsent {
+ struct _ftsent *fts_parent; /* parent directory */
+ struct _ftsent *fts_link; /* cycle or next file structure */
+ union {
+ long number; /* local numeric value */
+ void *pointer; /* local address value */
+ } fts_local;
+#define fts_number fts_local.number
+#define fts_pointer fts_local.pointer
+ char *fts_accpath; /* access path */
+ char *fts_path; /* root path */
+ int fts_cderr; /* chdir failed -- errno */
+ short fts_pathlen; /* strlen(fts_path) */
+ short fts_namelen; /* strlen(fts_name) */
+
+#define FTS_ROOTPARENTLEVEL -1
+#define FTS_ROOTLEVEL 0
+ short fts_level; /* depth (-1 to N) */
+
+#define FTS_D 1 /* preorder directory */
+#define FTS_DC 2 /* directory that causes cycles */
+#define FTS_DEFAULT 3 /* none of the above */
+#define FTS_DNR 4 /* unreadable directory */
+#define FTS_DP 5 /* postorder directory */
+#define FTS_ERR 6 /* error; errno is set */
+#define FTS_F 7 /* regular file */
+#define FTS_NS 8 /* stat(2) failed */
+#define FTS_NSOK 9 /* no stat(2) requested */
+#define FTS_SL 10 /* symbolic link */
+#define FTS_SLNONE 11 /* symbolic link without target */
+ u_short fts_info; /* user flags for FTSENT structure */
+
+#define FTS_AGAIN 1 /* read node again */
+#define FTS_FOLLOW 2 /* follow symbolic link */
+#define FTS_NOINSTR 3 /* no instructions */
+#define FTS_SKIP 4 /* discard node */
+ u_short fts_instr; /* fts_set() instructions */
+
+ struct stat fts_statb; /* stat(2) information */
+ char fts_name[1]; /* file name */
+} FTSENT;
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+FTSENT *fts_children __P((FTS *));
+int fts_close __P((FTS *));
+FTS *fts_open
+ __P((char * const *, int, int (*)(const FTSENT *, const FTSENT *)));
+FTSENT *fts_read __P((FTS *));
+int fts_set __P((FTS *, FTSENT *, int));
+__END_DECLS
+
+#endif /* !_FTS_H_ */
diff --git a/private/posix/programs/inc/bsd/ranlib.h b/private/posix/programs/inc/bsd/ranlib.h
new file mode 100644
index 000000000..d05873fdc
--- /dev/null
+++ b/private/posix/programs/inc/bsd/ranlib.h
@@ -0,0 +1,50 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * @(#)ranlib.h 5.2 (Berkeley) 4/3/91
+ */
+
+#ifndef _RANLIB_H_
+#define _RANLIB_H_
+
+#define RANLIBMAG "__.SYMDEF" /* archive file name */
+#define RANLIBSKEW 3 /* creation time offset */
+
+struct ranlib {
+ union {
+ off_t ran_strx; /* string table index */
+ char *ran_name; /* in memory symbol name */
+ } ran_un;
+ off_t ran_off; /* archive file offset */
+};
+
+#endif /* !_RANLIB_H_ */
diff --git a/private/posix/programs/inc/bsd/sys/cdefs.h b/private/posix/programs/inc/bsd/sys/cdefs.h
new file mode 100644
index 000000000..fbe572a44
--- /dev/null
+++ b/private/posix/programs/inc/bsd/sys/cdefs.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * @(#)cdefs.h 7.6 (Berkeley) 5/4/91
+ */
+
+#ifndef _CDEFS_H_
+#define _CDEFS_H_
+
+#if defined(__cplusplus)
+#define __BEGIN_DECLS extern "C" {
+#define __END_DECLS };
+#else
+#define __BEGIN_DECLS
+#define __END_DECLS
+#endif
+
+/*
+ * The __CONCAT macro is used to concatenate parts of symbol names, e.g.
+ * with "#define OLD(foo) __CONCAT(old,foo)", OLD(foo) produces oldfoo.
+ * The __CONCAT macro is a bit tricky -- make sure you don't put spaces
+ * in between its arguments. __CONCAT can also concatenate double-quoted
+ * strings produced by the __STRING macro, but this only works with ANSI C.
+ */
+#if defined(__STDC__) || defined(__cplusplus)
+#define __P(protos) protos /* full-blown ANSI C */
+#define __CONCAT(x,y) x ## y
+#define __STRING(x) #x
+
+#else /* !(__STDC__ || __cplusplus) */
+#define __P(protos) () /* traditional C preprocessor */
+#define __CONCAT(x,y) x/**/y
+#define __STRING(x) "x"
+
+#ifdef __GNUC__
+#define const __const /* GCC: ANSI C with -traditional */
+#define inline __inline
+#define signed __signed
+#define volatile __volatile
+
+#else /* !__GNUC__ */
+#define const /* delete ANSI C keywords */
+#define inline
+#define signed
+#define volatile
+#endif /* !__GNUC__ */
+#endif /* !(__STDC__ || __cplusplus) */
+
+#endif /* !_CDEFS_H_ */
diff --git a/private/posix/programs/inc/bsd/sys/mount.h b/private/posix/programs/inc/bsd/sys/mount.h
new file mode 100644
index 000000000..dc9bdaad6
--- /dev/null
+++ b/private/posix/programs/inc/bsd/sys/mount.h
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * @(#)mount.h 7.22 (Berkeley) 6/3/91
+ */
+
+#ifndef _POSIX_SOURCE
+typedef quad fsid_t; /* file system id type */
+#endif
+
+/*
+ * File identifier.
+ * These are unique per filesystem on a single machine.
+ */
+#define MAXFIDSZ 16
+
+struct fid {
+ u_short fid_len; /* length of data in bytes */
+ u_short fid_reserved; /* force longword alignment */
+ char fid_data[MAXFIDSZ]; /* data (variable length) */
+};
+
+/*
+ * file system statistics
+ */
+
+#define MNAMELEN 90 /* length of buffer for returned name */
+
+struct statfs {
+ short f_type; /* type of filesystem (see below) */
+ short f_flags; /* copy of mount flags */
+ long f_fsize; /* fundamental file system block size */
+ long f_bsize; /* optimal transfer block size */
+ long f_blocks; /* total data blocks in file system */
+ long f_bfree; /* free blocks in fs */
+ long f_bavail; /* free blocks avail to non-superuser */
+ long f_files; /* total file nodes in file system */
+ long f_ffree; /* free file nodes in fs */
+#ifdef _POSIX_SOURCE
+ struct _quad f_fsid;
+#else
+ fsid_t f_fsid; /* file system id */
+#endif
+ long f_spare[9]; /* spare for later */
+ char f_mntonname[MNAMELEN]; /* directory on which mounted */
+ char f_mntfromname[MNAMELEN];/* mounted filesystem */
+};
+
+/*
+ * File system types.
+ */
+#define MOUNT_NONE 0
+#define MOUNT_UFS 1
+#define MOUNT_NFS 2
+#define MOUNT_MFS 3
+#define MOUNT_PC 4
+#define MOUNT_MAXTYPE 4
+
+/*
+ * Structure per mounted file system.
+ * Each mounted file system has an array of
+ * operations and an instance record.
+ * The file systems are put on a doubly linked list.
+ */
+struct mount {
+ struct mount *mnt_next; /* next in mount list */
+ struct mount *mnt_prev; /* prev in mount list */
+ struct vfsops *mnt_op; /* operations on fs */
+ struct vnode *mnt_vnodecovered; /* vnode we mounted on */
+ struct vnode *mnt_mounth; /* list of vnodes this mount */
+ int mnt_flag; /* flags */
+ uid_t mnt_exroot; /* exported mapping for uid 0 */
+ struct statfs mnt_stat; /* cache of filesystem stats */
+ qaddr_t mnt_data; /* private data */
+};
+
+/*
+ * Mount flags.
+ */
+#define MNT_RDONLY 0x00000001 /* read only filesystem */
+#define MNT_SYNCHRONOUS 0x00000002 /* file system written synchronously */
+#define MNT_NOEXEC 0x00000004 /* can't exec from filesystem */
+#define MNT_NOSUID 0x00000008 /* don't honor setuid bits on fs */
+#define MNT_NODEV 0x00000010 /* don't interpret special files */
+
+/*
+ * exported mount flags.
+ */
+#define MNT_EXPORTED 0x00000100 /* file system is exported */
+#define MNT_EXRDONLY 0x00000200 /* exported read only */
+
+/*
+ * Flags set by internal operations.
+ */
+#define MNT_LOCAL 0x00001000 /* filesystem is stored locally */
+#define MNT_QUOTA 0x00002000 /* quotas are enabled on filesystem */
+
+/*
+ * Mask of flags that are visible to statfs()
+ */
+#define MNT_VISFLAGMASK 0x0000ffff
+
+/*
+ * filesystem control flags.
+ *
+ * MNT_MLOCK lock the mount entry so that name lookup cannot proceed
+ * past the mount point. This keeps the subtree stable during mounts
+ * and unmounts.
+ */
+#define MNT_UPDATE 0x00010000 /* not a real mount, just an update */
+#define MNT_MLOCK 0x00100000 /* lock so that subtree is stable */
+#define MNT_MWAIT 0x00200000 /* someone is waiting for lock */
+#define MNT_MPBUSY 0x00400000 /* scan of mount point in progress */
+#define MNT_MPWANT 0x00800000 /* waiting for mount point */
+#define MNT_UNMOUNT 0x01000000 /* unmount in progress */
+
+/*
+ * Operations supported on mounted file system.
+ */
+#ifdef KERNEL
+#ifdef __STDC__
+struct nameidata;
+#endif
+
+struct vfsops {
+ int (*vfs_mount) __P((struct mount *mp, char *path, caddr_t data,
+ struct nameidata *ndp, struct proc *p));
+ int (*vfs_start) __P((struct mount *mp, int flags,
+ struct proc *p));
+ int (*vfs_unmount) __P((struct mount *mp, int mntflags,
+ struct proc *p));
+ int (*vfs_root) __P((struct mount *mp, struct vnode **vpp));
+ /* int uid, should be uid_t */
+ int (*vfs_quotactl) __P((struct mount *mp, int cmds, int uid,
+ caddr_t arg, struct proc *p));
+ int (*vfs_statfs) __P((struct mount *mp, struct statfs *sbp,
+ struct proc *p));
+ int (*vfs_sync) __P((struct mount *mp, int waitfor));
+ int (*vfs_fhtovp) __P((struct mount *mp, struct fid *fhp,
+ struct vnode **vpp));
+ int (*vfs_vptofh) __P((struct vnode *vp, struct fid *fhp));
+ int (*vfs_init) __P(());
+};
+
+#define VFS_MOUNT(MP, PATH, DATA, NDP, P) \
+ (*(MP)->mnt_op->vfs_mount)(MP, PATH, DATA, NDP, P)
+#define VFS_START(MP, FLAGS, P) (*(MP)->mnt_op->vfs_start)(MP, FLAGS, P)
+#define VFS_UNMOUNT(MP, FORCE, P) (*(MP)->mnt_op->vfs_unmount)(MP, FORCE, P)
+#define VFS_ROOT(MP, VPP) (*(MP)->mnt_op->vfs_root)(MP, VPP)
+#define VFS_QUOTACTL(MP,C,U,A,P) (*(MP)->mnt_op->vfs_quotactl)(MP, C, U, A, P)
+#define VFS_STATFS(MP, SBP, P) (*(MP)->mnt_op->vfs_statfs)(MP, SBP, P)
+#define VFS_SYNC(MP, WAITFOR) (*(MP)->mnt_op->vfs_sync)(MP, WAITFOR)
+#define VFS_FHTOVP(MP, FIDP, VPP) (*(MP)->mnt_op->vfs_fhtovp)(MP, FIDP, VPP)
+#define VFS_VPTOFH(VP, FIDP) (*(VP)->v_mount->mnt_op->vfs_vptofh)(VP, FIDP)
+#endif /* KERNEL */
+
+/*
+ * Flags for various system call interfaces.
+ *
+ * forcibly flags for vfs_umount().
+ * waitfor flags to vfs_sync() and getfsstat()
+ */
+#define MNT_FORCE 1
+#define MNT_NOFORCE 2
+#define MNT_WAIT 1
+#define MNT_NOWAIT 2
+
+/*
+ * Generic file handle
+ */
+struct fhandle {
+#ifdef _POSIX_SOURCE
+ struct _quad fh_fsid;
+#else
+ fsid_t fh_fsid; /* File system id of mount point */
+#endif
+ struct fid fh_fid; /* Id of file */
+};
+typedef struct fhandle fhandle_t;
+
+/*
+ * Arguments to mount UFS
+ */
+struct ufs_args {
+ char *fspec; /* block special device to mount */
+ int exflags; /* export related flags */
+ uid_t exroot; /* mapping for root uid */
+};
+
+#ifdef MFS
+/*
+ * Arguments to mount MFS
+ */
+struct mfs_args {
+ char *name; /* name to export for statfs */
+ caddr_t base; /* base address of file system in memory */
+ u_long size; /* size of file system */
+};
+#endif /* MFS */
+
+#ifdef NFS
+/*
+ * File Handle (32 bytes for version 2), variable up to 1024 for version 3
+ */
+union nfsv2fh {
+ fhandle_t fh_generic;
+ u_char fh_bytes[32];
+};
+typedef union nfsv2fh nfsv2fh_t;
+
+/*
+ * Arguments to mount NFS
+ */
+struct nfs_args {
+ struct sockaddr *addr; /* file server address */
+ int sotype; /* Socket type */
+ int proto; /* and Protocol */
+ nfsv2fh_t *fh; /* File handle to be mounted */
+ int flags; /* flags */
+ int wsize; /* write size in bytes */
+ int rsize; /* read size in bytes */
+ int timeo; /* initial timeout in .1 secs */
+ int retrans; /* times to retry send */
+ char *hostname; /* server's name */
+};
+/*
+ * NFS mount option flags
+ */
+#define NFSMNT_SOFT 0x0001 /* soft mount (hard is default) */
+#define NFSMNT_WSIZE 0x0002 /* set write size */
+#define NFSMNT_RSIZE 0x0004 /* set read size */
+#define NFSMNT_TIMEO 0x0008 /* set initial timeout */
+#define NFSMNT_RETRANS 0x0010 /* set number of request retrys */
+#define NFSMNT_HOSTNAME 0x0020 /* set hostname for error printf */
+#define NFSMNT_INT 0x0040 /* allow interrupts on hard mount */
+#define NFSMNT_NOCONN 0x0080 /* Don't Connect the socket */
+#define NFSMNT_SCKLOCK 0x0100 /* Lock socket against others */
+#define NFSMNT_WANTSCK 0x0200 /* Want a socket lock */
+#define NFSMNT_SPONGY 0x0400 /* spongy mount (soft for stat and lookup) */
+#define NFSMNT_COMPRESS 0x0800 /* Compress nfs rpc xdr */
+#define NFSMNT_LOCKBITS (NFSMNT_SCKLOCK | NFSMNT_WANTSCK)
+#endif /* NFS */
+
+#ifdef KERNEL
+/*
+ * exported vnode operations
+ */
+void vfs_remove __P((struct mount *mp)); /* remove a vfs from mount list */
+int vfs_lock __P((struct mount *mp)); /* lock a vfs */
+void vfs_unlock __P((struct mount *mp)); /* unlock a vfs */
+struct mount *getvfs __P((fsid_t *fsid)); /* return vfs given fsid */
+struct mount *rootfs; /* ptr to root mount structure */
+struct vfsops *vfssw[]; /* mount filesystem type table */
+
+#else /* KERNEL */
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+int fstatfs __P((int, struct statfs *));
+int getfh __P((const char *, fhandle_t *));
+int getfsstat __P((struct statfs *, long, int));
+int getmntinfo __P((struct statfs **, int));
+int mount __P((int, const char *, int, void *));
+int statfs __P((const char *, struct statfs *));
+int unmount __P((const char *, int));
+__END_DECLS
+
+#endif /* KERNEL */
diff --git a/private/posix/programs/inc/bsd/tzfile.h b/private/posix/programs/inc/bsd/tzfile.h
new file mode 100644
index 000000000..43f5642ac
--- /dev/null
+++ b/private/posix/programs/inc/bsd/tzfile.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Arthur David Olson of the National Cancer Institute.
+ *
+ * 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.
+ *
+ * @(#)tzfile.h 5.10 (Berkeley) 4/3/91
+ */
+
+#ifndef _TZFILE_H_
+#define _TZFILE_H_
+
+/*
+ * Information about time zone files.
+ */
+ /* Time zone object file directory */
+#define TZDIR "/usr/share/zoneinfo"
+#define TZDEFAULT "/etc/localtime"
+#define TZDEFRULES "posixrules"
+
+/*
+** Each file begins with. . .
+*/
+
+struct tzhead {
+ char tzh_reserved[24]; /* reserved for future use */
+ char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
+ char tzh_leapcnt[4]; /* coded number of leap seconds */
+ char tzh_timecnt[4]; /* coded number of transition times */
+ char tzh_typecnt[4]; /* coded number of local time types */
+ char tzh_charcnt[4]; /* coded number of abbr. chars */
+};
+
+/*
+** . . .followed by. . .
+**
+** tzh_timecnt (char [4])s coded transition times a la time(2)
+** tzh_timecnt (unsigned char)s types of local time starting at above
+** tzh_typecnt repetitions of
+** one (char [4]) coded GMT offset in seconds
+** one (unsigned char) used to set tm_isdst
+** one (unsigned char) that's an abbreviation list index
+** tzh_charcnt (char)s '\0'-terminated zone abbreviations
+** tzh_leapcnt repetitions of
+** one (char [4]) coded leap second transition times
+** one (char [4]) total correction after above
+** tzh_ttisstdcnt (char)s indexed by type; if TRUE, transition
+** time is standard time, if FALSE,
+** transition time is wall clock time
+** if absent, transition times are
+** assumed to be wall clock time
+*/
+
+/*
+** In the current implementation, "tzset()" refuses to deal with files that
+** exceed any of the limits below.
+*/
+
+/*
+** The TZ_MAX_TIMES value below is enough to handle a bit more than a
+** year's worth of solar time (corrected daily to the nearest second) or
+** 138 years of Pacific Presidential Election time
+** (where there are three time zone transitions every fourth year).
+*/
+#define TZ_MAX_TIMES 370
+
+#define NOSOLAR /* 4BSD doesn't currently handle solar time */
+
+#ifndef NOSOLAR
+#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
+#else
+#define TZ_MAX_TYPES 10 /* Maximum number of local time types */
+#endif
+
+#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
+
+#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
+
+#define SECSPERMIN 60
+#define MINSPERHOUR 60
+#define HOURSPERDAY 24
+#define DAYSPERWEEK 7
+#define DAYSPERNYEAR 365
+#define DAYSPERLYEAR 366
+#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
+#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY)
+#define MONSPERYEAR 12
+
+#define TM_SUNDAY 0
+#define TM_MONDAY 1
+#define TM_TUESDAY 2
+#define TM_WEDNESDAY 3
+#define TM_THURSDAY 4
+#define TM_FRIDAY 5
+#define TM_SATURDAY 6
+
+#define TM_JANUARY 0
+#define TM_FEBRUARY 1
+#define TM_MARCH 2
+#define TM_APRIL 3
+#define TM_MAY 4
+#define TM_JUNE 5
+#define TM_JULY 6
+#define TM_AUGUST 7
+#define TM_SEPTEMBER 8
+#define TM_OCTOBER 9
+#define TM_NOVEMBER 10
+#define TM_DECEMBER 11
+
+#define TM_YEAR_BASE 1900
+
+#define EPOCH_YEAR 1970
+#define EPOCH_WDAY TM_THURSDAY
+
+/*
+** Accurate only for the past couple of centuries;
+** that will probably do.
+*/
+
+#define isleap(y) (((y) % 4) == 0 && ((y) % 100) != 0 || ((y) % 400) == 0)
+
+#endif /* !_TZFILE_H_ */
diff --git a/private/posix/programs/inc/bsd/utmp.h b/private/posix/programs/inc/bsd/utmp.h
new file mode 100644
index 000000000..8dc3c6daf
--- /dev/null
+++ b/private/posix/programs/inc/bsd/utmp.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 1988 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * @(#)utmp.h 5.11 (Berkeley) 4/3/91
+ */
+
+#ifndef _UTMP_H_
+#define _UTMP_H_
+
+#define _PATH_UTMP "/var/run/utmp"
+#define _PATH_WTMP "/var/log/wtmp"
+#define _PATH_LASTLOG "/var/log/lastlog"
+
+#define UT_NAMESIZE 8
+#define UT_LINESIZE 8
+#define UT_HOSTSIZE 16
+
+struct lastlog {
+ time_t ll_time;
+ char ll_line[UT_LINESIZE];
+ char ll_host[UT_HOSTSIZE];
+};
+
+struct utmp {
+ char ut_line[UT_LINESIZE];
+ char ut_name[UT_NAMESIZE];
+ char ut_host[UT_HOSTSIZE];
+ long ut_time;
+};
+
+#endif /* !_UTMP_H_ */
diff --git a/private/posix/programs/inc/df/bsdlib.h b/private/posix/programs/inc/df/bsdlib.h
new file mode 100644
index 000000000..8eb017118
--- /dev/null
+++ b/private/posix/programs/inc/df/bsdlib.h
@@ -0,0 +1,28 @@
+#include <dirent.h>
+#include <types.h>
+
+extern void bcopy (const char *__src, char *__dest, int __len);
+extern void bzero (char *__ptr, int __len);
+extern int fnmatch (const char *__pattern, const char *__string, int flags);
+extern mode_t getmode (void *__bbox, mode_t __omode);
+extern int getopt (int __nargc, char * const *__nargv, const char *__ostr);
+extern char *index (const char *__s, char __c);
+extern int isascii (int __c);
+#if 0
+extern int lstat (const char *__path, struct stat *__buf);
+#endif
+extern int mknod (const char *__path, mode_t __mode, int __dev);
+extern char *rindex (const char *__s, char __c);
+extern void *setmode (register char *__p);
+extern int scandir (const char *__dirname, struct dirent ***__namelist,
+ int (*__select) (struct dirent *),
+ int (*__dcomp) (const void *, const void *));
+extern void seekdir (DIR *__dirp, long __loc);
+extern int snprintf (char *__str, size_t __n, const char *__fmt, ...);
+extern void strmode (mode_t __mode, char *__p);
+extern long telldir (DIR *__dirp);
+extern int toascii (int __c);
+extern int utimes (const char *__file, struct timeval *__tvp);
+
+#define isascii(c) ((((c) & 0x7F) == (c)) ? 1 : 0)
+#define toascii(c) ((c) & 0x7F)
diff --git a/private/posix/programs/inc/df/misc.h b/private/posix/programs/inc/df/misc.h
new file mode 100644
index 000000000..63b2e7f1d
--- /dev/null
+++ b/private/posix/programs/inc/df/misc.h
@@ -0,0 +1,99 @@
+#include <limits.h>
+
+#define MACHINE "i386"
+
+#define quad "struct _quad"
+#if 0
+#define emalloc malloc
+#endif
+
+#define MAXNAMLEN NAME_MAX
+#define MAXPATHLEN PATH_MAX
+
+/*
+* File system parameters and macros //DF_DSC May not be meaningful in NT
+*
+* The file system is made out of blocks of at most MAXBSIZE units, with
+* smaller units (fragments) only in the last direct block. MAXBSIZE
+* primarily determines the size of buffers in the buffer pool. It may be
+* made larger without any effect on existing file systems; however making
+* it smaller make make some file systems unmountable.
+*/
+ #define MAXBSIZE 8192
+ #define MAXFRAG 8
+
+
+#define BLOCK_SIZE 512
+#define FD_SETSIZE 256
+
+/* fnmatch() */
+#define FNM_PATHNAME 0x01 /* match pathnames, not filenames */
+#define FNM_QUOTE 0x02 /* escape special chars with \ */
+
+typedef unsigned long fd_set;
+#define NFDBITS 32
+
+#define FD_SET(n, p) (*p |= (1 << ((n) % NFDBITS)))
+#define FD_CLR(n, p) (*p &= ~(1 << ((n) % NFDBITS)))
+#define FD_ISSET(n, p) (*p & (1 << ((n) % NFDBITS)))
+
+typedef unsigned char u_char;
+typedef unsigned int u_int;
+typedef unsigned short u_short;
+typedef unsigned long u_long;
+struct _quad { long val[2]; };
+
+typedef long * qaddr_t; /* should be typedef quad * qaddr_t; */
+extern int optind; /* character checked for validity */
+extern char *optarg; /* argument associated with option */
+
+
+#define LITTLE_ENDIAN 1
+#define BYTE_ORDER LITTLE_ENDIAN
+union wait {
+ int w_status; /* used in syscall */
+ /*
+ * Terminated process status.
+ */
+ struct {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ unsigned int w_termsig:7, /* termination signal */
+ w_Coredump:1, /* core dump indicator */
+ w_retcode:8, /* exit code if w_termsig==0 */
+ w_Filler:16; /* upper bits filler */
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ unsigned int w_Filler:16, /* upper bits filler */
+ w_retcode:8, /* exit code if w_termsig==0 */
+ w_Coredump:1, /* core dump indicator */
+ w_termsig:7; /* termination signal */
+#endif
+ } w_T;
+ /*
+ * Stopped process status. Returned
+ * only for traced children unless requested
+ * with the WUNTRACED option bit.
+ */
+ struct {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ unsigned int w_Stopval:8, /* == W_STOPPED if stopped */
+ w_Stopsig:8, /* signal that stopped us */
+ w_Filler:16; /* upper bits filler */
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ unsigned int w_Filler:16, /* upper bits filler */
+ w_Stopsig:8, /* signal that stopped us */
+ w_Stopval:8; /* == W_STOPPED if stopped */
+#endif
+ } w_S;
+};
+
+
+/*
+ * Structure returned by gettimeofday(2) system call,
+ * and used in other calls.
+ */
+struct timeval {
+ long tv_sec; /* seconds */
+ long tv_usec; /* and microseconds */
+};
diff --git a/private/posix/programs/inc/local.h b/private/posix/programs/inc/local.h
new file mode 100644
index 000000000..c75c0fc56
--- /dev/null
+++ b/private/posix/programs/inc/local.h
@@ -0,0 +1,86 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * 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.
+ *
+ * @(#)local.h 5.2 (Berkeley) 2/5/91
+ */
+
+/*
+ * Information local to this implementation of stdio,
+ * in particular, macros and private variables.
+ */
+
+int __sflush __P((FILE *));
+FILE *__sfp __P((void));
+int __srefill __P((FILE *));
+int __sread __P((void *, char *, int));
+int __swrite __P((void *, char const *, int));
+fpos_t __sseek __P((void *, fpos_t, int));
+int __sclose __P((void *));
+void __sinit __P((void));
+void _cleanup __P((void));
+void (*__cleanup) __P((void));
+void __smakebuf __P((FILE *));
+int _fwalk __P((int (*)(FILE *)));
+int __swsetup __P((FILE *));
+int __sflags __P((const char *, int *));
+
+extern int __sdidinit;
+
+/*
+ * Return true iff the given FILE cannot be written now.
+ */
+#define cantwrite(fp) \
+ ((((fp)->_flags & __SWR) == 0 || (fp)->_bf._base == NULL) && \
+ __swsetup(fp))
+
+/*
+ * Test whether the given stdio file has an active ungetc buffer;
+ * release such a buffer, without restoring ordinary unread data.
+ */
+#define HASUB(fp) ((fp)->_ub._base != NULL)
+#define FREEUB(fp) { \
+ if ((fp)->_ub._base != (fp)->_ubuf) \
+ free((char *)(fp)->_ub._base); \
+ (fp)->_ub._base = NULL; \
+}
+
+/*
+ * test for an fgetline() buffer.
+ */
+#define HASLB(fp) ((fp)->_lb._base != NULL)
+#define FREELB(fp) { \
+ free((char *)(fp)->_lb._base); \
+ (fp)->_lb._base = NULL; \
+}
diff --git a/private/posix/programs/inc/pwd.h b/private/posix/programs/inc/pwd.h
new file mode 100644
index 000000000..7c78d5324
--- /dev/null
+++ b/private/posix/programs/inc/pwd.h
@@ -0,0 +1,85 @@
+/*-
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * @(#)pwd.h 5.13 (Berkeley) 5/28/91
+ */
+
+#ifndef _PWD_H_
+#define _PWD_H_
+
+#include <sys/types.h>
+
+#ifndef _POSIX_SOURCE
+#define _PATH_PASSWD "/etc/passwd"
+#define _PATH_MASTERPASSWD "/etc/master.passwd"
+
+#define _PATH_MP_DB "/etc/pwd.db"
+#define _PATH_SMP_DB "/etc/spwd.db"
+
+#define _PATH_PWD_MKDB "/usr/sbin/pwd_mkdb"
+
+#define _PW_KEYBYNAME '1' /* stored by name */
+#define _PW_KEYBYNUM '2' /* stored by entry in the "file" */
+#define _PW_KEYBYUID '3' /* stored by uid */
+
+#define _PASSWORD_EFMT1 '_' /* extended encryption format */
+
+#define _PASSWORD_LEN 128 /* max length, not counting NULL */
+#endif
+
+struct passwd {
+ char *pw_name; /* user name */
+ char *pw_passwd; /* encrypted password */
+ int pw_uid; /* user uid */
+ int pw_gid; /* user gid */
+ time_t pw_change; /* password change time */
+ char *pw_class; /* user access class */
+ char *pw_gecos; /* Honeywell login info */
+ char *pw_dir; /* home directory */
+ char *pw_shell; /* default shell */
+ time_t pw_expire; /* account expiration */
+};
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+struct passwd *getpwuid __P((uid_t));
+struct passwd *getpwnam __P((const char *));
+#ifndef _POSIX_SOURCE
+struct passwd *getpwent __P((void));
+int setpassent __P((int));
+int setpwent __P((void));
+void endpwent __P((void));
+#endif
+__END_DECLS
+
+#endif /* !_PWD_H_ */
diff --git a/private/posix/programs/pax/append.c b/private/posix/programs/pax/append.c
new file mode 100644
index 000000000..b7cb0d043
--- /dev/null
+++ b/private/posix/programs/pax/append.c
@@ -0,0 +1,97 @@
+/* $Source: /u/mark/src/pax/RCS/append.c,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * append.c - append to a tape archive.
+ *
+ * DESCRIPTION
+ *
+ * Routines to allow appending of archives
+ *
+ * AUTHORS
+ *
+ * Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
+ *
+ *
+ * Sponsored by The USENIX Association for public distribution.
+ *
+ * Copyright (c) 1989 Mark H. Colburn.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice is 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 Mark H. Colburn and sponsored by The
+ * USENIX Association.
+ *
+ * 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.
+ *
+ * $Log: append.c,v $
+ * Revision 1.2 89/02/12 10:03:58 mark
+ * 1.2 release fixes
+ *
+ * Revision 1.1 88/12/23 18:02:00 mark
+ * Initial revision
+ *
+ */
+
+#ifndef lint
+static char *ident = "$Id: append.c,v 1.2 89/02/12 10:03:58 mark Exp $";
+static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n";
+#endif /* ! lint */
+
+
+/* Headers */
+
+#include "pax.h"
+
+
+/* append_archive - main loop for appending to a tar archive
+ *
+ * DESCRIPTION
+ *
+ * Append_archive reads an archive until the end of the archive is
+ * reached once the archive is reached, the buffers are reset and the
+ * create_archive function is called to handle the actual writing of
+ * the appended archive data. This is quite similar to the
+ * read_archive function, however, it does not do all the processing.
+ */
+
+
+#ifdef __STDC__
+
+void append_archive(void)
+
+#else
+
+void append_archive()
+
+#endif
+{
+ Stat sb;
+ char name[PATH_MAX + 1];
+
+ name[0] = '\0';
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: void append_archive() in append.c\n");
+#endif
+ while (get_header(name, &sb) == 0) {
+ if (((ar_format == TAR)
+ ? buf_skip(ROUNDUP((OFFSET) sb.sb_size, BLOCKSIZE))
+ : buf_skip((OFFSET) sb.sb_size)) < 0) {
+ warn(name, "File data is corrupt");
+ }
+ }
+ /* we have now gotten to the end of the archive... */
+
+ /* reset the buffer now that we have read the entire archive */
+ bufend = bufidx = bufstart;
+#if 0
+ bufidx -= 2*blocksize;
+ total -= 2*blocksize;
+#endif
+ create_archive();
+}
diff --git a/private/posix/programs/pax/buffer.c b/private/posix/programs/pax/buffer.c
new file mode 100644
index 000000000..8090bbd01
--- /dev/null
+++ b/private/posix/programs/pax/buffer.c
@@ -0,0 +1,949 @@
+/* $Source: /u/mark/src/pax/RCS/buffer.c,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * buffer.c - Buffer management functions
+ *
+ * DESCRIPTION
+ *
+ * These functions handle buffer manipulations for the archiving
+ * formats. Functions are provided to get memory for buffers,
+ * flush buffers, read and write buffers and de-allocate buffers.
+ * Several housekeeping functions are provided as well to get some
+ * information about how full buffers are, etc.
+ *
+ * AUTHOR
+ *
+ * Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
+ *
+ * Sponsored by The USENIX Association for public distribution.
+ *
+ * Copyright (c) 1989 Mark H. Colburn.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice is 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 Mark H. Colburn and sponsored by The
+ * USENIX Association.
+ *
+ * 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.
+ *
+ * $Log: buffer.c,v $
+ * Revision 1.2 89/02/12 10:04:02 mark
+ * 1.2 release fixes
+ *
+ * Revision 1.1 88/12/23 18:02:01 mark
+ * Initial revision
+ *
+ */
+
+#ifndef lint
+static char *ident = "$Id: buffer.c,v 1.2 89/02/12 10:04:02 mark Exp $";
+static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n";
+#endif /* ! lint */
+
+
+/* Headers */
+
+#include "pax.h"
+
+
+/* Function Prototypes */
+
+#ifdef __STDC__
+
+static int ar_write(int, char *, uint);
+static void buf_pad(OFFSET);
+static int indata(int, OFFSET, char *);
+static void outflush(void);
+static void buf_use(uint);
+static int buf_in_avail(char **, uint *);
+static uint buf_out_avail(char **);
+
+#else /* !__STDC__ */
+
+static int ar_write();
+static void buf_pad();
+static int indata();
+static void outflush();
+static void buf_use();
+static int buf_in_avail();
+static uint buf_out_avail();
+
+#endif /* __STDC__ */
+
+
+/* inentry - install a single archive entry
+ *
+ * DESCRIPTION
+ *
+ * Inentry reads an archive entry from the archive file and writes it
+ * out the the named file. If we are in PASS mode during archive
+ * processing, the pass() function is called, otherwise we will
+ * extract from the archive file.
+ *
+ * Inentry actaully calls indata to process the actual data to the
+ * file.
+ *
+ * PARAMETERS
+ *
+ * char *name - name of the file to extract from the archive
+ * Stat *asb - stat block of the file to be extracted from the
+ * archive.
+ *
+ * RETURNS
+ *
+ * Returns zero if successful, -1 otherwise.
+ */
+
+#ifdef __STDC__
+
+int inentry(char *name, Stat *asb)
+
+#else
+
+int inentry(name, asb)
+char *name;
+Stat *asb;
+
+#endif
+{
+ Link *linkp;
+ int ifd;
+ int ofd;
+#ifdef _POSIX_SOURCE /* Xn */
+ struct utimbuf tstamp; /* Xn */
+#else /* Xn */
+ time_t tstamp[2];
+#endif /* Xn */
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int inentry() in buffer.c\n");
+#endif
+ if ((ofd = openout(name, asb, linkp = linkfrom(name, asb), 0)) > 0) {
+ if (asb->sb_size || linkp == (Link *)NULL || linkp->l_size == 0) {
+ close(indata(ofd, asb->sb_size, name));
+ } else if ((ifd = open(linkp->l_path->p_name, O_RDONLY)) < 0) {
+ warn(linkp->l_path->p_name, strerror(errno)); /* Xn */
+ } else {
+ passdata(linkp->l_path->p_name, ifd, name, ofd);
+ close(ifd);
+ close(ofd);
+ }
+ } else {
+ return(buf_skip((OFFSET) asb->sb_size) >= 0);
+ }
+#ifdef _POSIX_SOURCE
+ tstamp.actime = (!f_pass && f_access_time) ? asb->sb_atime : time((time_t *) 0); /* Xn */
+ tstamp.modtime = f_mtime ? asb->sb_mtime : time((time_t *) 0); /* Xn */
+ (void) utime(name, &tstamp); /* Xn */
+#else
+ tstamp[0] = (!f_pass && f_access_time) ? asb->sb_atime : time((time_t *) 0);
+ tstamp[1] = f_mtime ? asb->sb_mtime : time((time_t *) 0);
+ (void) utime(name, tstamp); /* Xn */
+#endif
+ return (0);
+}
+
+
+/* outdata - write archive data
+ *
+ * DESCRIPTION
+ *
+ * Outdata transfers data from the named file to the archive buffer.
+ * It knows about the file padding which is required by tar, but no
+ * by cpio. Outdata continues after file read errors, padding with
+ * null characters if neccessary. Closes the input file descriptor
+ * when finished.
+ *
+ * PARAMETERS
+ *
+ * int fd - file descriptor of file to read data from
+ * char *name - name of file
+ * OFFSET size - size of the file
+ *
+ */
+
+#ifdef __STDC__
+
+void outdata(int fd, char *name, OFFSET size)
+
+#else
+
+void outdata(fd, name, size)
+int fd;
+char *name;
+OFFSET size;
+
+#endif
+{
+ uint chunk;
+ int got;
+ int oops;
+ uint avail;
+ int pad;
+ char *buf;
+
+ oops = got = 0;
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: void outdata() in buffer.c\n");
+#endif
+ if (pad = (size % BLOCKSIZE)) {
+ pad = (BLOCKSIZE - pad);
+ }
+ while (size) {
+ avail = buf_out_avail(&buf);
+//printf("avail X%dX, size X%dX\n", avail, size);
+ size -= (chunk = size < avail ? (uint) size : avail);
+//printf("chunk X%dX", chunk);
+memset(buf, 0, chunk);
+ if (oops == 0 && (got = read(fd, buf, (unsigned int) chunk)) < 0 && errno != 12) {
+ oops = -1;
+//puts("b warn outdata");
+//printf("size X%dX\n", sizeof(buf));
+//printf("oops X%dX got X%dX fd X%dX buf X%sX errno X%dX X%dX\n", oops, got, fd, buf, errno, chunk);
+ warn(name, strerror(errno)); /* Xn */
+//puts("a warn outdata");
+ got = 0;
+ }
+
+ if(got == -1 && errno == 12)
+ got = 0;
+
+ if (got < chunk) {
+ if (oops == 0) {
+ oops = -1;
+ }
+ warn(name, "Early EOF");
+ while (got < chunk) {
+ buf[got++] = '\0';
+ }
+ }
+ buf_use(chunk);
+ }
+ close(fd);
+ if (ar_format == TAR) {
+ buf_pad((OFFSET) pad);
+ }
+}
+
+
+/* write_eot - write the end of archive record(s)
+ *
+ * DESCRIPTION
+ *
+ * Write out an End-Of-Tape record. We actually zero at least one
+ * record, through the end of the block. Old tar writes garbage after
+ * two zeroed records -- and PDtar used to.
+ */
+
+#ifdef __STDC__
+
+void write_eot(void)
+
+#else
+
+void write_eot()
+
+#endif
+{
+ OFFSET pad;
+ char header[M_STRLEN + H_STRLEN + 1];
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: void write_eot() in buffer.c\n");
+#endif
+ if (ar_format == TAR) {
+ /* write out two zero blocks for trailer */
+ pad = 2 * BLOCKSIZE;
+ } else {
+ if (pad = (total + M_STRLEN + H_STRLEN + TRAILZ) % BLOCKSIZE) {
+ pad = BLOCKSIZE - pad;
+ }
+ strcpy(header, M_ASCII);
+ sprintf(header + M_STRLEN, H_PRINT, 0, 0,
+#if 0 /* NIST-PCTS */
+ 0, 0, 0, 1, 0, (time_t) 0, TRAILZ, pad);
+#else /* NIST-PCTS */
+ 0, 0, 0, 1, 0, (time_t) 0, TRAILZ, (OFFSET) 0); /* NIST-PCTS */
+#endif /* NIST-PCTS */
+ outwrite(header, M_STRLEN + H_STRLEN);
+ outwrite(TRAILER, TRAILZ);
+ }
+ buf_pad((OFFSET) pad);
+ outflush();
+}
+
+
+/* outwrite - write archive data
+ *
+ * DESCRIPTION
+ *
+ * Writes out data in the archive buffer to the archive file. The
+ * buffer index and the total byte count are incremented by the number
+ * of data bytes written.
+ *
+ * PARAMETERS
+ *
+ * char *idx - pointer to data to write
+ * uint len - length of the data to write
+ */
+
+#ifdef __STDC__
+
+void outwrite(char *idx, uint len)
+
+#else
+
+void outwrite(idx, len)
+char *idx; /* pointer to data to write */
+uint len; /* length of data to write */
+
+#endif
+{
+ uint have;
+ uint want;
+ char *endx;
+
+ endx = idx + len;
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: void outwrite() in buffer.c\n");
+#endif
+ while (want = endx - idx) {
+ if (bufend - bufidx < 0) {
+ fatal("Buffer overflow in out_write\n"); /* Xn */
+ }
+//puts("b outflush");
+ if ((have = bufend - bufidx) == 0) {
+ outflush();
+//puts("a outflush");
+ }
+ if (have > want) {
+ have = want;
+ }
+ memcpy(bufidx, idx, (int) have);
+//puts("a memcpy outwrite");
+ bufidx += have;
+ idx += have;
+ total += have;
+ }
+}
+
+
+/* passdata - copy data to one file
+ *
+ * DESCRIPTION
+ *
+ * Copies a file from one place to another. Doesn't believe in input
+ * file descriptor zero (see description of kludge in openin() comments).
+ * Closes the provided output file descriptor.
+ *
+ * PARAMETERS
+ *
+ * char *from - input file name (old file)
+ * int ifd - input file descriptor
+ * char *to - output file name (new file)
+ * int ofd - output file descriptor
+ */
+
+#ifdef __STDC__
+
+void passdata(char *from, int ifd, char *to, int ofd)
+
+#else
+
+void passdata(from, ifd, to, ofd)
+char *from;
+int ifd;
+char *to;
+int ofd;
+
+#endif
+{
+ int got;
+ int sparse;
+ char block[BUFSIZ];
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: void passdata() in buffer.c\n");
+#endif
+ if (ifd) {
+ lseek(ifd, (OFFSET) 0, SEEK_SET); /* Xn */
+ sparse = 0;
+ while ((got = read(ifd, block, sizeof(block))) > 0
+ && (sparse = ar_write(ofd, block, (uint) got)) >= 0) {
+ total += got;
+ }
+ if (got) {
+ warn(got < 0 ? from : to, strerror(errno)); /* Xn */
+ } else if (sparse > 0
+ && (lseek(ofd, (OFFSET)(-sparse), SEEK_CUR) < 0 /* Xn */
+ || write(ofd, block, (uint) sparse) != sparse)) {
+ warn(to, strerror(errno)); /* Xn */
+ }
+ }
+ close(ofd);
+}
+
+
+/* buf_allocate - get space for the I/O buffer
+ *
+ * DESCRIPTION
+ *
+ * buf_allocate allocates an I/O buffer using malloc. The resulting
+ * buffer is used for all data buffering throughout the program.
+ * Buf_allocate must be called prior to any use of the buffer or any
+ * of the buffering calls.
+ *
+ * PARAMETERS
+ *
+ * int size - size of the I/O buffer to request
+ *
+ * ERRORS
+ *
+ * If an invalid size is given for a buffer or if a buffer of the
+ * required size cannot be allocated, then the function prints out an
+ * error message and returns a non-zero exit status to the calling
+ * process, terminating the program.
+ *
+ */
+
+#ifdef __STDC__
+
+void buf_allocate(OFFSET size)
+
+#else
+
+void buf_allocate(size)
+OFFSET size;
+
+#endif
+{
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: void buf_allocate() in buffer.c\n");
+#endif
+ if (size <= 0) {
+ fatal("invalid value for blocksize");
+ }
+ if ((bufstart = malloc((unsigned) size)) == (char *)NULL) {
+ fatal("Cannot allocate I/O buffer");
+ }
+ bufend = bufidx = bufstart;
+ bufend += size;
+}
+
+
+/* buf_skip - skip input archive data
+ *
+ * DESCRIPTION
+ *
+ * Buf_skip skips past archive data. It is used when the length of
+ * the archive data is known, and we do not wish to process the data.
+ *
+ * PARAMETERS
+ *
+ * OFFSET len - number of bytes to skip
+ *
+ * RETURNS
+ *
+ * Returns zero under normal circumstances, -1 if unreadable data is
+ * encountered.
+ */
+
+#ifdef __STDC__
+
+int buf_skip(OFFSET len)
+
+#else
+
+int buf_skip(len)
+OFFSET len;
+
+#endif
+{
+ uint chunk;
+ int corrupt = 0;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int buf_skip() in buffer.c\n");
+#endif
+ while (len) {
+ if (bufend - bufidx < 0) {
+ fatal("Buffer overflow in buf_skip\n"); /* Xn */
+ }
+ while ((chunk = bufend - bufidx) == 0) {
+ corrupt |= ar_read();
+ }
+ if (chunk > len) {
+ chunk = len;
+ }
+ bufidx += chunk;
+ len -= chunk;
+ total += chunk;
+ }
+ return (corrupt);
+}
+
+
+/* buf_read - read a given number of characters from the input archive
+ *
+ * DESCRIPTION
+ *
+ * Reads len number of characters from the input archive and
+ * stores them in the buffer pointed at by dst.
+ *
+ * PARAMETERS
+ *
+ * char *dst - pointer to buffer to store data into
+ * uint len - length of data to read
+ *
+ * RETURNS
+ *
+ * Returns zero with valid data, -1 if unreadable portions were
+ * replaced by null characters.
+ */
+
+#ifdef __STDC__
+
+int buf_read(char *dst, uint len)
+
+#else
+
+int buf_read(dst, len)
+char *dst;
+uint len;
+
+#endif
+{
+ int have;
+ int want;
+ int corrupt = 0;
+ char *endx = dst + len;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int buf_read() in buffer.c\n");
+#endif
+ while (want = endx - dst) {
+ if (bufend - bufidx < 0) {
+ fatal("Buffer overflow in buf_read\n"); /* Xn */
+ }
+ while ((have = bufend - bufidx) == 0) {
+ have = 0;
+ corrupt |= ar_read();
+ }
+ if (have > want) {
+ have = want;
+ }
+ memcpy(dst, bufidx, have);
+ bufidx += have;
+ dst += have;
+ total += have;
+ }
+ return (corrupt);
+}
+
+
+/* indata - install data from an archive
+ *
+ * DESCRIPTION
+ *
+ * Indata writes size bytes of data from the archive buffer to the output
+ * file specified by fd. The filename which is being written, pointed
+ * to by name is provided only for diagnostics.
+ *
+ * PARAMETERS
+ *
+ * int fd - output file descriptor
+ * OFFSET size - number of bytes to write to output file
+ * char *name - name of file which corresponds to fd
+ *
+ * RETURNS
+ *
+ * Returns given file descriptor.
+ */
+
+#ifdef __STDC__
+
+static int indata(int fd, OFFSET size, char *name)
+
+#else
+
+static int indata(fd, size, name)
+int fd;
+OFFSET size;
+char *name;
+
+#endif
+{
+ uint chunk;
+ char *oops;
+ int sparse;
+ int corrupt;
+ char *buf;
+ uint avail;
+
+ corrupt = sparse = 0;
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static int indata() in buffer.c\n");
+#endif
+ oops = (char *)NULL;
+ while (size) {
+ corrupt |= buf_in_avail(&buf, &avail);
+ size -= (chunk = size < avail ? (uint) size : avail);
+ if (oops == (char *)NULL && (sparse = ar_write(fd, buf, chunk)) < 0) {
+ oops = strerror(errno); /* Xn */
+ }
+ buf_use(chunk);
+ }
+ if (corrupt) {
+ warn(name, "Corrupt archive data");
+ }
+ if (oops) {
+ warn(name, oops);
+ } else if (sparse > 0 && (lseek(fd, (OFFSET) - 1, SEEK_CUR) < 0 /* Xn */
+ || write(fd, "", 1) != 1)) {
+ warn(name, strerror(errno)); /* Xn */
+ }
+ return (fd);
+}
+
+
+/* outflush - flush the output buffer
+ *
+ * DESCRIPTION
+ *
+ * The output buffer is written, if there is anything in it, to the
+ * archive file.
+ */
+
+#ifdef __STDC__
+
+void outflush(void)
+
+#else
+
+void outflush()
+
+#endif
+{
+ char *buf;
+ int got;
+ uint len;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: void outflush() in buffer.c\n");
+#endif
+ /* if (bufidx - buf > 0) */
+ for (buf = bufstart; len = bufidx - buf;) {
+#ifdef _POSIX_SOURCE /* 7/3/90-JPB */
+ if ((got = write(archivefd, buf, MIN(len, blocksize))) > 0)
+ buf += got;
+
+ if (got <= 0 || got != MIN(len, blocksize))
+ next(AR_WRITE);
+#else
+ if ((got = write(archivefd, buf, MIN(len, blocksize))) > 0) {
+ buf += got;
+ } else if (got <= 0) {
+ next(AR_WRITE);
+ }
+#endif
+ }
+ bufend = (bufidx = bufstart) + blocksize;
+}
+
+
+/* ar_read - fill the archive buffer
+ *
+ * DESCRIPTION
+ *
+ * Remembers mid-buffer read failures and reports them the next time
+ * through. Replaces unreadable data with null characters. Resets
+ * the buffer pointers as appropriate.
+ *
+ * RETURNS
+ *
+ * Returns zero with valid data, -1 otherwise.
+ */
+
+#ifdef __STDC__
+
+int ar_read(void)
+
+#else
+
+int ar_read()
+
+#endif
+{
+ int got;
+ static int failed = 0; /* Xn */
+
+ bufend = bufidx = bufstart;
+/*
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int ar_read() in buffer.c\n");
+#endif
+*/
+ if (!failed) {
+ if (areof) {
+ if (total == 0) {
+ fatal("No input");
+ } else {
+ next(AR_READ);
+ }
+ }
+ while (!failed && !areof && bufstart + blocksize - bufend >= blocksize) {
+ if ((got = read(archivefd, bufend, (unsigned int) blocksize)) > 0) {
+ bufend += got;
+#ifdef _POSIX_SOURCE /* 7/3/90-JPB */
+ if (got != blocksize)
+ ++areof;
+#endif
+ } else if (got < 0) {
+ failed = -1;
+ warnarch(strerror(errno), (OFFSET) 0 - (bufend - bufidx)); /* Xn */
+ } else {
+ ++areof;
+ }
+ }
+ }
+ if (failed && bufend == bufstart) {
+ failed = 0;
+ for (got = 0; got < blocksize; ++got) {
+ *bufend++ = '\0';
+ }
+ return (-1);
+ }
+ return (0);
+}
+
+
+/* ar_write - write a filesystem block
+ *
+ * DESCRIPTION
+ *
+ * Writes len bytes of data data from the specified buffer to the
+ * specified file. Seeks past sparse blocks.
+ *
+ * PARAMETERS
+ *
+ * int fd - file to write to
+ * char *buf - buffer to get data from
+ * uint len - number of bytes to transfer
+ *
+ * RETURNS
+ *
+ * Returns 0 if the block was written, the given length for a sparse
+ * block or -1 if unsuccessful.
+ */
+
+#ifdef __STDC__
+
+int ar_write(int fd, char *buf, uint len)
+
+#else
+
+int ar_write(fd, buf, len)
+int fd;
+char *buf;
+uint len;
+
+#endif
+{
+ char *bidx;
+ char *bend;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int ar_write() in buffer.c\n");
+#endif
+ bend = (bidx = buf) + len;
+ while (bidx < bend) {
+ if (*bidx++) {
+ return (write(fd, buf, len) == len ? 0 : -1);
+ }
+ }
+ return (lseek(fd, (OFFSET) len, SEEK_CUR) < 0 ? -1 : len); /* Xn */
+}
+
+
+/* buf_pad - pad the archive buffer
+ *
+ * DESCRIPTION
+ *
+ * Buf_pad writes len zero bytes to the archive buffer in order to
+ * pad it.
+ *
+ * PARAMETERS
+ *
+ * OFFSET pad - number of zero bytes to pad
+ *
+ */
+
+#ifdef __STDC__
+
+static void buf_pad(OFFSET pad)
+
+#else
+
+static void buf_pad(pad)
+OFFSET pad;
+
+#endif
+{
+ int idx;
+ int have;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static void buf_pad() in buffer.c\n");
+#endif
+ while (pad) {
+ if ((have = bufend - bufidx) > pad) {
+ have = pad;
+ }
+ for (idx = 0; idx < have; ++idx) {
+ *bufidx++ = '\0';
+ }
+ total += have;
+ pad -= have;
+ if (bufend - bufidx == 0) {
+ outflush();
+ }
+ }
+}
+
+
+/* buf_use - allocate buffer space
+ *
+ * DESCRIPTION
+ *
+ * Buf_use marks space in the buffer as being used; advancing both the
+ * buffer index (bufidx) and the total byte count (total).
+ *
+ * PARAMETERS
+ *
+ * uint len - Amount of space to allocate in the buffer
+ */
+
+#ifdef __STDC__
+
+static void buf_use(uint len)
+
+#else
+
+static void buf_use(len)
+uint len;
+
+#endif
+{
+ bufidx += len;
+ total += len;
+}
+
+
+/* buf_in_avail - index available input data within the buffer
+ *
+ * DESCRIPTION
+ *
+ * Buf_in_avail fills the archive buffer, and points the bufp
+ * parameter at the start of the data. The lenp parameter is
+ * modified to contain the number of bytes which were read.
+ *
+ * PARAMETERS
+ *
+ * char **bufp - pointer to the buffer to read data into
+ * uint *lenp - pointer to the number of bytes which were read
+ * (returned to the caller)
+ *
+ * RETURNS
+ *
+ * Stores a pointer to the data and its length in given locations.
+ * Returns zero with valid data, -1 if unreadable portions were
+ * replaced with nulls.
+ *
+ * ERRORS
+ *
+ * If an error occurs in ar_read, the error code is returned to the
+ * calling function.
+ *
+ */
+
+#ifdef __STDC__
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static void buf_use() in buffer.c\n");
+#endif
+static int buf_in_avail(char **bufp, uint *lenp)
+
+#else
+
+static int buf_in_avail(bufp, lenp)
+char **bufp;
+uint *lenp;
+
+#endif
+{
+ uint have;
+ int corrupt = 0;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static int buf_in_avail() in buffer.c\n");
+#endif
+ while ((have = bufend - bufidx) == 0) {
+ corrupt |= ar_read();
+ }
+ *bufp = bufidx;
+ *lenp = have;
+ return (corrupt);
+}
+
+
+/* buf_out_avail - index buffer space for archive output
+ *
+ * DESCRIPTION
+ *
+ * Stores a buffer pointer at a given location. Returns the number
+ * of bytes available.
+ *
+ * PARAMETERS
+ *
+ * char **bufp - pointer to the buffer which is to be stored
+ *
+ * RETURNS
+ *
+ * The number of bytes which are available in the buffer.
+ *
+ */
+
+#ifdef __STDC__
+
+static uint buf_out_avail(char **bufp)
+
+#else
+
+static uint buf_out_avail(bufp)
+char **bufp;
+
+#endif
+{
+ int have;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static uint buf_out_avail() in buffer.c\n");
+#endif
+//printf("bufend X%dX bufidx X%dX\n", bufend, bufidx);
+ if (bufend - bufidx < 0) {
+//puts("buf_out_avail fata");
+ fatal("Buffer overflow in buf_out_avail\n"); /* Xn */
+ }
+ if ((have = bufend - bufidx) == 0) {
+ outflush();
+ }
+ *bufp = bufidx;
+//printf("buf_out_avail rets X%dX\n", have);
+ return (have);
+}
diff --git a/private/posix/programs/pax/config.h b/private/posix/programs/pax/config.h
new file mode 100644
index 000000000..05f336e41
--- /dev/null
+++ b/private/posix/programs/pax/config.h
@@ -0,0 +1,193 @@
+/* $Source: /u/mark/src/pax/RCS/config.h,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * config.h - configuration options for PAX
+ *
+ * DESCRIPTION
+ *
+ * This file contains a number of configurable parameters for the
+ * PAX software. This files should be edited prior to makeing the
+ * package.
+ *
+ * AUTHOR
+ *
+ * Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
+ *
+ * Sponsored by The USENIX Association for public distribution.
+ *
+ * Copyright (c) 1989 Mark H. Colburn.
+ * 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 Mark H. Colburn and sponsored by The USENIX Association.
+ *
+ * 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 _PAX_CONFIG_H
+#define _PAX_CONFIG_H
+
+/* Defines */
+
+/* XENIX_286 (SCO ugh, Xenix system V(?) 286, USG with changes...
+ * You will get a warning about DIRSIZ being redefined, ignore it,
+ * complain to SCO about include files that are messed up or send
+ * mail to doug@lentni.UUCP, who can provide some patches to fix
+ * your include files.
+ *
+ * Defining XENIX_286 will automatically define USG.
+ *
+ */
+#if 0
+#define XENIX_286 /* Running on a XENIX 286 system */
+#endif
+
+/*
+ * USG - USG (UNIX System V) specific modifications Xn
+ *
+ * Define USG if you are running UNIX System V or some similar variant Xn
+ */
+#define USG /* Running on a USG System */
+
+/*
+ * BSD - BSD (Berkeley) specific modifications Xn
+ *
+ * Define BSD if you are running some version of BSD UNIX Xn
+ */
+#if 0
+#define BSD /* Running on a BSD System */
+#endif
+
+/*
+ * DEF_AR_FILE - tar only (required)
+ *
+ * DEF_AR_FILE should contain the full pathname of your favorite archive
+ * device. Normally this would be a tape drive, but it may be a disk drive
+ * on those systems that don't have tape drives.
+ */
+#define DEF_AR_FILE "/dev/qic" /* The default archive on your system */
+
+/*
+ * TTY - device which interactive queries should be directed to (required)
+ *
+ * This is the device to which interactive queries will be sent to and
+ * received from. On most unix systems, this should be /dev/tty, however, on
+ * some systems, such as MS-DOS, it my need to be different (e.g. "con:").
+ */
+#define TTY "/dev/tty" /* for most versions of UNIX */
+#if 0
+#define TTY "con:" /* For MS-DOS */
+#endif
+
+/*
+ * PAXDIR - if you do not have directory access routines
+ *
+ * Define PAXDIR if you do not have Doug Gwyn's dirent package installed
+ * as a system library or you wish to use the version supplied with PAX.
+ *
+ * NOTE: DO NOT DEFINE THIS IF YOU HAVE BERKELEY DIRECTORY ACCESS ROUTINES.
+ */
+#if 0
+#define PAXDIR /* use paxdir.h paxdir.c */
+#endif
+
+/*
+ * DIRENT - directory access routines (required)
+ *
+ * If you have Doug Gwyn's dirent package installed, either as a system
+ * library, or are using the paxdir.c and paxdir.h routines which come with
+ * PAX, then define dirent.
+ *
+ * NOTE: DO NOT DEFINE THIS IF YOU HAVE BERKELEY DIRECTORY ACCESS ROUTINES.
+ */
+#define DIRENT /* use POSIX compatible directory routines */
+
+/*
+ * OFFSET - compiler dependent offset type
+ *
+ * OFFSET is the type which is returned by lseek(). It is different on
+ * some systems. Most define it to be off_t, but some define it to be long.
+ */
+#define OFFSET off_t /* for most BSD, USG and other systems */
+#if 0
+#define OFFSET long /* for most of the rest of them... */
+#endif
+
+/*
+ * VOID - compiler support for VOID types
+ *
+ * If your system does not support void, then this should be defined to
+ * int, otherwise, it should be left undefined.
+ *
+ * For ANSI Systems this should always be blank.
+ */
+#ifndef __STDC__
+/* #define void int /* for system which do support void */
+#endif
+
+/*
+ * SIG_T - return type for the signal routine
+ *
+ * Some systems have signal defines to return an int *, other return a
+ * void *. Please choose the correct value for your system.
+ */
+#define SIG_T void /* signal defined as "void (*signal)()" */
+#if 0
+#define SIG_T int /* signal defined as "int (*signal)()" */
+#endif
+
+/*
+ * STRCSPN - use the strcspn function included with pax
+ *
+ * Some systems do not have the strcspn() function in their C libraries.
+ * For those system define STRCSPN and the one provided in regexp.c will
+ * be used.
+ */
+#if 0
+#define STRCSPN /* implementation does not have strcspn() */
+#endif
+
+/*
+ * STRERROR - use the strerror function included with pax
+ *
+ * Non-Ansi systems do not have the strerror() function in their C libraries.
+ * For those system define STRERROR and the one provided in warn.c will Xn
+ * be used instead.
+ */
+#ifndef __STDC__
+#define STRERROR /* implementation does not have strerror() */
+#endif
+/*
+
+/*
+ * END OF CONFIGURATION SECTION
+ *
+ * Nothing beyond this point should need to be changed
+ */
+
+#ifdef BSD
+#ifdef USG
+#include "You must first edit config.h and Makefile to configure pax."
+#endif
+#endif
+/*
+ * Do a little sanity checking
+ */
+#ifdef PAXDIR
+# ifndef DIRENT
+# define DIRENT
+# endif
+#endif
+
+#ifdef XENIX_286
+# define USG
+#endif /* XENIX_286 */
+
+#endif /* _PAX_CONFIG_H */
diff --git a/private/posix/programs/pax/cpio.c b/private/posix/programs/pax/cpio.c
new file mode 100644
index 000000000..0f5dffa38
--- /dev/null
+++ b/private/posix/programs/pax/cpio.c
@@ -0,0 +1,227 @@
+/* $Source: /u/mark/src/pax/RCS/cpio.c,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * cpio.c - Cpio specific functions for archive handling
+ *
+ * DESCRIPTION
+ *
+ * These function provide a cpio conformant interface to the pax
+ * program.
+ *
+ * AUTHOR
+ *
+ * Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
+ *
+ * Sponsored by The USENIX Association for public distribution.
+ *
+ * Copyright (c) 1989 Mark H. Colburn.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice is 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 Mark H. Colburn and sponsored by The
+ * USENIX Association.
+ *
+ * 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.
+ *
+ * $Log: cpio.c,v $
+ * Revision 1.2 89/02/12 10:04:13 mark
+ * 1.2 release fixes
+ *
+ * Revision 1.1 88/12/23 18:02:05 mark
+ * Initial revision
+ *
+ */
+
+#ifndef lint
+static char *ident = "$Id: cpio.c,v 1.2 89/02/12 10:04:13 mark Exp $";
+static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n";
+#endif /* ! lint */
+
+
+/* Headers */
+
+#include "pax.h"
+
+
+/* Function Prototypes */
+
+#ifdef __STDC__
+static void usage(void);
+#else /* !__STDC__ */
+static void usage();
+#endif /* __STDC__ */
+
+
+/* do_cpio - handle cpio format archives
+ *
+ * DESCRIPTION
+ *
+ * Do_cpio provides a standard CPIO interface to the PAX program. All
+ * of the standard cpio flags are available, and the behavior of the
+ * program mimics traditonal cpio.
+ *
+ * PARAMETERS
+ *
+ * int argc - command line argument count
+ * char **argv - pointer to command line arguments
+ *
+ * RETURNS
+ *
+ * Nothing.
+ */
+
+#ifdef __STDC__
+
+void do_cpio(int argc, char **argv) /* Xn */
+
+#else
+
+void do_cpio(argc, argv) /* Xn */
+int argc;
+char **argv;
+
+#endif
+{
+ int c;
+ char *dirname;
+ Stat st;
+
+ /* default input/output file for CPIO is STDIN/STDOUT */
+ ar_file = "-";
+ names_from_stdin = 1;
+
+ /* set up the flags to reflect the default CPIO inteface. */
+ blocksize = BLOCKSIZE;
+ ar_interface = CPIO;
+ ar_format = CPIO;
+ msgfile=stderr;
+
+#ifdef _POSIX2_SOURCE /* Xn */
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: void do_cpio() in cpio.c\n");
+#endif
+ while ((c = getopt(argc, (const char * const *) argv, "D:Bacdfilmoprtuv")) != EOF) { /* Xn */
+#else /* Xn */
+ while ((c = getopt(argc, argv, "D:Bacdfilmoprtuv")) != EOF) {
+#endif /* Xn */
+ switch (c) {
+ case 'i':
+ f_extract = 1;
+ break;
+ case 'o':
+ f_create = 1;
+ break;
+ case 'p':
+ f_pass = 1;
+ dirname = argv[--argc];
+
+ /* check to make sure that the argument is a directory */
+ if (LSTAT(dirname, &st) < 0) {
+ fatal(strerror(errno)); /* Xn */
+ }
+ if ((st.sb_mode & S_IFMT) != S_IFDIR) {
+ fatal("Not a directory");
+ }
+ break;
+ case 'B':
+ blocksize = BLOCK;
+ break;
+ case 'a':
+ f_access_time = 1;
+ break;
+ case 'c':
+ break;
+ case 'D':
+ ar_file = optarg;
+ break;
+ case 'd':
+ f_dir_create = 1;
+ break;
+ case 'f':
+ f_reverse_match = 1;
+ break;
+ case 'l':
+ f_link = 1;
+ break;
+ case 'm':
+ f_mtime = 1;
+ break;
+ case 'r':
+ f_interactive = 1;
+ break;
+ case 't':
+ f_list = 1;
+ break;
+ case 'u':
+ f_unconditional = 1;
+ break;
+ case 'v':
+ f_verbose = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (f_create + f_pass + f_extract != 1) {
+ usage();
+ }
+ if (!f_pass) {
+ buf_allocate((OFFSET) blocksize);
+ }
+ if (f_extract) {
+ open_archive(AR_READ); /* Open for reading */
+ read_archive();
+ } else if (f_create) {
+ open_archive(AR_WRITE);
+ create_archive();
+ } else if (f_pass) {
+ pass(dirname);
+ }
+
+ /* print out the total block count transfered */
+ fprintf(stderr, "%ld Blocks\n", ROUNDUP(total, BLOCKSIZE) / BLOCKSIZE);
+
+ exit(0);
+ /* NOTREACHED */
+}
+
+
+/* usage - print a helpful message and exit
+ *
+ * DESCRIPTION
+ *
+ * Usage prints out the usage message for the CPIO interface and then
+ * exits with a non-zero termination status. This is used when a user
+ * has provided non-existant or incompatible command line arguments.
+ *
+ * RETURNS
+ *
+ * Returns an exit status of 1 to the parent process.
+ *
+ */
+
+#ifdef __STDC__
+
+static void usage(void)
+
+#else
+
+static void usage()
+
+#endif
+{
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static void usage() in cpio.c\n");
+#endif
+ fprintf(stderr, "Usage: %s -o[Bacv]\n", myname);
+ fprintf(stderr, " %s -i[Bcdmrtuvf] [pattern...]\n", myname);
+ fprintf(stderr, " %s -p[adlmruv] directory\n", myname);
+ exit(1);
+}
diff --git a/private/posix/programs/pax/create.c b/private/posix/programs/pax/create.c
new file mode 100644
index 000000000..742fee464
--- /dev/null
+++ b/private/posix/programs/pax/create.c
@@ -0,0 +1,426 @@
+/* $Source: /u/mark/src/pax/RCS/create.c,v $
+ *
+ * $Revision: 1.3 $
+ *
+ * create.c - Create a tape archive.
+ *
+ * DESCRIPTION
+ *
+ * These functions are used to create/write and archive from an set of
+ * named files.
+ *
+ * AUTHOR
+ *
+ * Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
+ *
+ * Sponsored by The USENIX Association for public distribution.
+ *
+ * Copyright (c) 1989 Mark H. Colburn.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice is 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 Mark H. Colburn and sponsored by The
+ * USENIX Association.
+ *
+ * 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.
+ *
+ * $Log: create.c,v $
+ * Revision 1.3 89/02/12 10:29:37 mark
+ * Fixed misspelling of Replstr
+ *
+ * Revision 1.2 89/02/12 10:04:17 mark
+ * 1.2 release fixes
+ *
+ * Revision 1.1 88/12/23 18:02:06 mark
+ * Initial revision
+ *
+ */
+
+#ifndef lint
+static char *ident = "$Id: create.c,v 1.3 89/02/12 10:29:37 mark Exp Locker: mark $";
+static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n";
+#endif /* ! lint */
+
+
+/* Headers */
+
+#include "pax.h"
+
+
+/* Function Prototypes */
+
+#ifdef __STDC__
+
+static void writetar(char *, Stat *);
+static void writecpio(char *, Stat *);
+static char tartype(int);
+
+#else /* !__STDC__ */
+
+static void writetar();
+static void writecpio();
+static char tartype();
+
+#endif /* __STDC__ */
+
+
+/* create_archive - create a tar archive.
+ *
+ * DESCRIPTION
+ *
+ * Create_archive is used as an entry point to both create and append
+ * archives. Create archive goes through the files specified by the
+ * user and writes each one to the archive if it can. Create_archive
+ * knows how to write both cpio and tar headers and the padding which
+ * is needed for each type of archive.
+ *
+ * RETURNS
+ *
+ * Always returns 0
+ */
+
+#ifdef __STDC__
+
+int create_archive(void)
+
+#else
+
+int create_archive()
+
+#endif
+{
+ char name[PATH_MAX + 1];
+ Stat sb;
+ int fd;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int create_archive() in create.c\n");
+#endif
+ if (f_append) { /* Xn */
+ if (ar_format == TAR) { /* Xn */
+ (void) lseek(archivefd, -(OFFSET) (2*blocksize), SEEK_END); /* Xn */
+ } else { /* Xn */
+ OFFSET eoa; /* Xn */
+ /* Xn */
+ eoa = lseek(archivefd, (OFFSET) 0, SEEK_CUR); /* Xn */
+ (void) lseek(archivefd, -(eoa - total + 87), SEEK_END); /* Xn */
+ } /* Xn */
+ } /* Xn */
+ /* Xn */
+ while (name_next(name, &sb) != -1) {
+ if ((fd = openin(name, &sb)) < 0) {
+ /* FIXME: pax wants to exit here??? */
+ continue;
+ }
+
+ if (rplhead != (Replstr *)NULL) {
+ rpl_name(name);
+ if (strlen(name) == 0) {
+ continue;
+ }
+ }
+ if (get_disposition("add", name) || get_newname(name, sizeof(name))) {
+ /* skip file... */
+ if (fd) {
+ close(fd);
+ }
+ continue;
+ }
+
+ if (!f_link && sb.sb_nlink > 1) {
+#if 0 /* NIST-PCTS */
+ if (islink(name, &sb)) {
+#else /* NIST-PCTS */
+ if (ar_format == TAR && islink(name, &sb)) { /* NIST-PCTS */
+#endif /* NIST-PCTS */
+ sb.sb_size = 0;
+ }
+ linkto(name, &sb);
+ }
+ if (ar_format == TAR) {
+ writetar(name, &sb);
+ } else {
+ writecpio(name, &sb);
+ }
+ if (fd) {
+ outdata(fd, name, sb.sb_size);
+ }
+ if (f_verbose) {
+ print_entry(name, &sb);
+ }
+ }
+
+ write_eot();
+ close_archive();
+ return (0);
+}
+
+
+/* writetar - write a header block for a tar file
+ *
+ * DESCRIPTION
+ *
+ * Make a header block for the file name whose stat info is in st.
+ * Return header pointer for success, NULL if the name is too long.
+ *
+ * The tar header block is structured as follows:
+ *
+ * FIELD NAME OFFSET SIZE
+ * -------------|---------------|------
+ * name 0 100
+ * mode 100 8
+ * uid 108 8
+ * gid 116 8
+ * size 124 12
+ * mtime 136 12
+ * chksum 148 8
+ * typeflag 156 1
+ * linkname 157 100
+ * magic 257 6
+ * version 263 2
+ * uname 265 32
+ * gname 297 32
+ * devmajor 329 8
+ * devminor 337 8
+ * prefix 345 155
+ *
+ * PARAMETERS
+ *
+ * char *name - name of file to create a header block for
+ * Stat *asb - pointer to the stat structure for the named file
+ *
+ */
+
+#ifdef __STDC__
+
+static void writetar(char *name, Stat *asb)
+
+#else
+
+static void writetar(name, asb)
+char *name;
+Stat *asb;
+
+#endif
+{
+ char *p;
+ char *prefix = (char *)NULL;
+ int i;
+ int sum;
+ char hdr[BLOCKSIZE];
+ Link *from;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static void writetar() in create.c\n");
+#endif
+ memset(hdr, 0, BLOCKSIZE);
+ if (strlen(name) > 256) {
+ warn(name, "name too long");
+ return;
+ }
+
+ /*
+ * If the pathname is longer than TNAMLEN, but less than 256, then
+ * we can split it up into the prefix and the filename.
+ */
+
+ if (strlen(name) > 100) {
+ prefix = name;
+ for (;;) {
+ name = strchr(name + 1, '/');
+ if (NULL == name) {
+ warn(prefix, "Name too long");
+ return;
+ }
+
+ if (strlen(name + 1) <= 100) {
+ // fits
+ *name++ = '\0';
+ break;
+ }
+ }
+ }
+
+#ifdef S_IFLNK
+ if ((asb->sb_mode & S_IFMT) == S_IFLNK) {
+ strcpy(&hdr[157], asb->sb_link);
+ asb->sb_size = 0;
+ }
+#endif
+ strcpy(hdr, name);
+ sprintf(&hdr[100], "%06o \0", asb->sb_mode & ~S_IFMT);
+ sprintf(&hdr[108], "%06o \0", asb->sb_uid);
+ sprintf(&hdr[116], "%06o \0", asb->sb_gid);
+ sprintf(&hdr[124], "%011lo ", (long) asb->sb_size);
+ sprintf(&hdr[136], "%011lo ", (long) asb->sb_mtime);
+ strncpy(&hdr[148], " ", 8);
+ hdr[156] = tartype(asb->sb_mode);
+ if (asb->sb_nlink > 1 && (from = linkfrom(name, asb)) != (Link *)NULL) {
+
+ if (strlen(from->l_name) > 100) {
+ warn(name, "link name too long");
+ return;
+ }
+
+ strcpy(&hdr[157], from->l_name);
+ hdr[156] = LNKTYPE;
+ }
+
+ strcpy(&hdr[257], TMAGIC);
+ strncpy(&hdr[263], TVERSION, 2);
+ strcpy(&hdr[265], finduname((int) asb->sb_uid));
+ strcpy(&hdr[297], findgname((int) asb->sb_gid));
+#ifndef _POSIX_SOURCE
+ sprintf(&hdr[329], "%06o \0", major(asb->sb_rdev));
+ sprintf(&hdr[337], "%06o \0", minor(asb->sb_rdev));
+#endif
+ if (prefix != (char *)NULL) {
+ strncpy(&hdr[345], prefix, 155);
+ }
+
+ /* Calculate the checksum */
+
+ sum = 0;
+ p = hdr;
+ for (i = 0; i < 500; i++) {
+ sum += 0xFF & *p++;
+ }
+
+ /* Fill in the checksum field. */
+
+ sprintf(&hdr[148], "%06o \0", sum);
+
+ outwrite(hdr, BLOCKSIZE);
+}
+
+
+/* tartype - return tar file type from file mode
+ *
+ * DESCRIPTION
+ *
+ * tartype returns the character which represents the type of file
+ * indicated by "mode".
+ *
+ * PARAMETERS
+ *
+ * int mode - file mode from a stat block
+ *
+ * RETURNS
+ *
+ * The character which represents the particular file type in the
+ * ustar standard headers.
+ */
+
+#ifdef __STDC__
+
+static char tartype(int mode)
+
+#else
+
+static char tartype(mode)
+int mode;
+
+#endif
+{
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static char tartype() in create.c\n");
+#endif
+ switch (mode & S_IFMT) {
+
+#ifdef S_IFCTG
+ case S_IFCTG:
+ return(CONTTYPE);
+#endif
+
+ case S_IFDIR:
+ return (DIRTYPE);
+
+#ifdef S_IFLNK
+ case S_IFLNK:
+ return (SYMTYPE);
+#endif
+
+#ifdef S_IFIFO /* Xn */
+ case S_IFIFO:
+ return (FIFOTYPE);
+#endif
+
+#ifdef S_IFCHR
+ case S_IFCHR:
+ return (CHRTYPE);
+#endif
+
+#ifdef S_IFBLK
+ case S_IFBLK:
+ return (BLKTYPE);
+#endif
+ /* Xn */
+#ifdef S_IFSOCK /* Xn */
+ case S_IFSOCK: /* Xn */
+ return (SOCKTYPE); /* Xn */
+#endif /* Xn */
+
+ default:
+ return (REGTYPE);
+ }
+}
+
+
+/* writecpio - write a cpio archive header
+ *
+ * DESCRIPTION
+ *
+ * Writes a new CPIO style archive header for the file specified.
+ *
+ * PARAMETERS
+ *
+ * char *name - name of file to create a header block for
+ * Stat *asb - pointer to the stat structure for the named file
+ */
+
+#ifdef __STDC__
+
+static void writecpio(char *name, Stat *asb)
+
+#else
+
+static void writecpio(name, asb)
+char *name;
+Stat *asb;
+
+#endif
+{
+ uint namelen;
+ char header[M_STRLEN + H_STRLEN + 1];
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static void writecpio() in create.c\n");
+#endif
+ namelen = (uint) strlen(name) + 1;
+ strcpy(header, M_ASCII);
+
+ sprintf(header + M_STRLEN, "%06o%06o%06o%06o%06o",
+ USH(asb->sb_dev), USH(asb->sb_ino), USH(asb->sb_mode),
+ USH(asb->sb_uid), USH(asb->sb_gid));
+ sprintf(header + M_STRLEN + 30, "%06o%06o%011lo%06o%011lo",
+ USH(asb->sb_nlink),
+#ifndef _POSIX_SOURCE
+ USH(asb->sb_rdev),
+#else
+ USH(0),
+#endif
+ f_mtime ? asb->sb_mtime : time((time_t *) 0),
+ namelen, asb->sb_size);
+ outwrite(header, M_STRLEN + H_STRLEN);
+ outwrite(name, namelen);
+#ifdef S_IFLNK
+ if ((asb->sb_mode & S_IFMT) == S_IFLNK) {
+ outwrite(asb->sb_link, (uint) asb->sb_size);
+ }
+#endif /* S_IFLNK */
+}
diff --git a/private/posix/programs/pax/extract.c b/private/posix/programs/pax/extract.c
new file mode 100644
index 000000000..a637199c4
--- /dev/null
+++ b/private/posix/programs/pax/extract.c
@@ -0,0 +1,650 @@
+/* $Source: /u/mark/src/pax/RCS/extract.c,v $
+ *
+ * $Revision: 1.3 $
+ *
+ * extract.c - Extract files from a tar archive.
+ *
+ * DESCRIPTION
+ *
+ * AUTHOR
+ *
+ * Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
+ *
+ * Sponsored by The USENIX Association for public distribution.
+ *
+ * Copyright (c) 1989 Mark H. Colburn.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice is 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 Mark H. Colburn and sponsored by The
+ * USENIX Association.
+ *
+ * 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.
+ *
+ * $Log: extract.c,v $
+ * Revision 1.3 89/02/12 10:29:43 mark
+ * Fixed misspelling of Replstr
+ *
+ * Revision 1.2 89/02/12 10:04:24 mark
+ * 1.2 release fixes
+ *
+ * Revision 1.1 88/12/23 18:02:07 mark
+ * Initial revision
+ *
+ */
+
+#ifndef lint
+static char *ident = "$Id: extract.c,v 1.3 89/02/12 10:29:43 mark Exp Locker: mark $";
+static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n";
+#endif /* ! lint */
+
+
+/* Headers */
+
+#include "pax.h"
+
+
+/* Defines */
+
+/*
+ * Swap bytes.
+ */
+#define SWAB(n) ((((ushort)(n) >> 8) & 0xff) | (((ushort)(n) << 8) & 0xff00))
+
+
+/* Function Prototypes */
+
+#ifdef __STDC__
+
+static int inbinary(char *, char *, Stat *);
+static int inascii(char *, char *, Stat *);
+static int inswab(char *, char *, Stat *);
+static int readtar(char *, Stat *);
+static int readcpio(char *, Stat *);
+
+#else /* !__STDC__ */
+
+static int inbinary();
+static int inascii();
+static int inswab();
+static int readtar();
+static int readcpio();
+
+#endif /* __STDC__ */
+
+
+/* read_archive - read in an archive
+ *
+ * DESCRIPTION
+ *
+ * Read_archive is the central entry point for reading archives.
+ * Read_archive determines the proper archive functions to call
+ * based upon the archive type being processed.
+ *
+ * RETURNS
+ *
+ */
+
+#ifdef __STDC__
+
+void read_archive(void) /* Xn */
+
+#else
+
+void read_archive() /* Xn */
+
+#endif
+{
+ Stat sb;
+ char name[PATH_MAX + 1];
+ int match;
+ int pad;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: void read_archive() in extract.c\n");
+#endif
+ name_gather(); /* get names from command line */
+ name[0] = '\0';
+ while (get_header(name, &sb) == 0) {
+ match = name_match(name) ^ f_reverse_match;
+ if (f_list) { /* only wanted a table of contents */
+ if (match) {
+ print_entry(name, &sb);
+ }
+ if (((ar_format == TAR)
+ ? buf_skip(ROUNDUP((OFFSET) sb.sb_size, BLOCKSIZE))
+ : buf_skip((OFFSET) sb.sb_size)) < 0) {
+ warn(name, "File data is corrupt");
+ }
+ } else if (match) {
+ if (rplhead != (Replstr *)NULL) {
+ rpl_name(name);
+ if (strlen(name) == 0) {
+ continue;
+ }
+ }
+ if (get_disposition("extract", name) ||
+ get_newname(name, sizeof(name))) {
+ /* skip file... */
+ if (((ar_format == TAR)
+ ? buf_skip(ROUNDUP((OFFSET) sb.sb_size, BLOCKSIZE))
+ : buf_skip((OFFSET) sb.sb_size)) < 0) {
+ warn(name, "File data is corrupt");
+ }
+ continue;
+ }
+ if (inentry(name, &sb) < 0) {
+ warn(name, "File data is corrupt");
+ }
+ if (f_verbose) {
+ print_entry(name, &sb);
+ }
+#if 0 /* NIST-PCTS */
+ if (ar_format == TAR && sb.sb_nlink > 1)
+#else /* NIST-PCTS */
+ if (ar_format == TAR && sb.sb_nlink > 1 && /* NIST-PCTS */
+ (sb.sb_mode & S_IFMT) != S_IFDIR) /* NIST-PCTS */
+#endif /* NIST-PCTS */
+ {
+ /*
+ * This kludge makes sure that the link table is cleared
+ * before attempting to process any other links.
+ */
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: () in extract.c\n");
+#endif
+ if (sb.sb_nlink > 1) {
+ linkfrom(name, &sb);
+ }
+ }
+ if (ar_format == TAR && (pad = sb.sb_size % BLOCKSIZE) != 0) {
+ pad = BLOCKSIZE - pad;
+ buf_skip((OFFSET) pad);
+ }
+ } else {
+ if (((ar_format == TAR)
+ ? buf_skip(ROUNDUP((OFFSET) sb.sb_size, BLOCKSIZE))
+ : buf_skip((OFFSET) sb.sb_size)) < 0) {
+ warn(name, "File data is corrupt");
+ }
+ }
+ }
+ close_archive();
+}
+
+
+
+/* get_header - figures which type of header needs to be read.
+ *
+ * DESCRIPTION
+ *
+ * This is merely a single entry point for the two types of archive
+ * headers which are supported. The correct header is selected
+ * depending on the archive type.
+ *
+ * PARAMETERS
+ *
+ * char *name - name of the file (passed to header routine)
+ * Stat *asb - Stat block for the file (passed to header routine)
+ *
+ * RETURNS
+ *
+ * Returns the value which was returned by the proper header
+ * function.
+ */
+
+#ifdef __STDC__
+
+int get_header(char *name, Stat *asb)
+
+#else
+
+int get_header(name, asb)
+char *name;
+Stat *asb;
+
+#endif
+{
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int get_header() in extract.c\n");
+#endif
+ if (ar_format == TAR) {
+ return(readtar(name, asb));
+ } else {
+ return(readcpio(name, asb));
+ }
+}
+
+
+/* readtar - read a tar header
+ *
+ * DESCRIPTION
+ *
+ * Tar_head read a tar format header from the archive. The name
+ * and asb parameters are modified as appropriate for the file listed
+ * in the header. Name is assumed to be a pointer to an array of
+ * at least PATH_MAX bytes.
+ *
+ * PARAMETERS
+ *
+ * char *name - name of the file for which the header is
+ * for. This is modified and passed back to
+ * the caller.
+ * Stat *asb - Stat block for the file for which the header
+ * is for. The fields of the stat structure are
+ * extracted from the archive header. This is
+ * also passed back to the caller.
+ *
+ * RETURNS
+ *
+ * Returns 0 if a valid header was found, or -1 if EOF is
+ * encountered.
+ */
+
+#ifdef __STDC__
+
+static int readtar(char *name, Stat *asb)
+
+#else
+
+static int readtar(name, asb)
+char *name;
+Stat *asb;
+
+#endif
+{
+ int status = 3; /* Initial status at start of archive */
+ static int prev_status;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static int readtar() in extract.c\n");
+#endif
+ for (;;) {
+ prev_status = status;
+ status = read_header(name, asb);
+ switch (status) {
+ case 1: /* Valid header */
+ return(0);
+ case 0: /* Invalid header */
+ switch (prev_status) {
+ case 3: /* Error on first record */
+ warn(ar_file, "This doesn't look like a tar archive");
+ /* FALLTHRU */
+ case 2: /* Error after record of zeroes */
+ case 1: /* Error after header rec */
+ warn(ar_file, "Skipping to next file...");
+ /* FALLTHRU */
+ default:
+ case 0: /* Error after error */
+ break;
+ }
+ break;
+
+ case 2: /* Record of zeroes */
+ case EOF: /* End of archive */
+ default:
+ return(-1);
+ }
+ }
+}
+
+
+/* readcpio - read a CPIO header
+ *
+ * DESCRIPTION
+ *
+ * Read in a cpio header. Understands how to determine and read ASCII,
+ * binary and byte-swapped binary headers. Quietly translates
+ * old-fashioned binary cpio headers (and arranges to skip the possible
+ * alignment byte). Returns zero if successful, -1 upon archive trailer.
+ *
+ * PARAMETERS
+ *
+ * char *name - name of the file for which the header is
+ * for. This is modified and passed back to
+ * the caller.
+ * Stat *asb - Stat block for the file for which the header
+ * is for. The fields of the stat structure are
+ * extracted from the archive header. This is
+ * also passed back to the caller.
+ *
+ * RETURNS
+ *
+ * Returns 0 if a valid header was found, or -1 if EOF is
+ * encountered.
+ */
+
+#ifdef __STDC__
+
+static int readcpio(char *name, Stat *asb)
+
+#else
+
+static int readcpio(name, asb)
+char *name;
+Stat *asb;
+
+#endif
+{
+ OFFSET skipped;
+ char magic[M_STRLEN];
+ static int align = 0; /* Xn */
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static int readcpio() in extract.c\n");
+#endif
+ if (align > 0) {
+ buf_skip((OFFSET) align);
+ }
+ align = 0;
+ for (;;) {
+ buf_read(magic, M_STRLEN);
+ skipped = 0;
+ while ((align = inascii(magic, name, asb)) < 0
+ && (align = inbinary(magic, name, asb)) < 0
+ && (align = inswab(magic, name, asb)) < 0) {
+ if (++skipped == 1) {
+ if (total - sizeof(magic) == 0) {
+ fatal("Unrecognizable archive");
+ }
+ warnarch("Bad magic number", (OFFSET) sizeof(magic));
+ if (name[0]) {
+ warn(name, "May be corrupt");
+ }
+ }
+ memcpy(magic, magic + 1, sizeof(magic) - 1);
+ buf_read(magic + sizeof(magic) - 1, 1);
+ }
+ if (skipped) {
+ warnarch("Apparently resynchronized", (OFFSET) sizeof(magic));
+ warn(name, "Continuing");
+ }
+ if (strcmp(name, TRAILER) == 0) {
+ return (-1);
+ }
+ if (nameopt(name) >= 0) {
+ break;
+ }
+#if 0 /* NIST-PCTS */
+ buf_skip((OFFSET) asb->sb_size + align);
+#else /* NIST-PCTS */
+ if (asb->sb_nlink > 1 && islink(name, asb)) /* NIST-PCTS */
+ buf_skip((OFFSET) 0 + align); /* NIST-PCTS */
+ else /* NIST-PCTS */
+ buf_skip((OFFSET) asb->sb_size + align); /* NIST-PCTS */
+#endif /* NIST-PCTS */
+ }
+#ifdef S_IFLNK
+ if ((asb->sb_mode & S_IFMT) == S_IFLNK) {
+ if (buf_read(asb->sb_link, (uint) asb->sb_size) < 0) {
+ warn(name, "Corrupt symbolic link");
+ return (readcpio(name, asb));
+ }
+ asb->sb_link[asb->sb_size] = '\0';
+ asb->sb_size = 0;
+ }
+#endif /* S_IFLNK */
+
+ /* destroy absolute pathnames for security reasons */
+ if (name[0] == '/') {
+ if (name[1]) {
+ while (name[0] = name[1]) {
+ ++name;
+ }
+ } else {
+ name[0] = '.';
+ }
+ }
+ asb->sb_atime = asb->sb_ctime = asb->sb_mtime;
+#if 0 /* NIST-PCTS */
+ if (asb->sb_nlink > 1) {
+#else /* NIST-PCTS */
+ if (asb->sb_nlink > 1 && (asb->sb_mode & S_IFMT) != S_IFDIR) { /* NIST-PCTS */
+#endif /* NIST-PCTS */
+ linkto(name, asb);
+ }
+ return (0);
+}
+
+
+/* inswab - read a reversed by order binary header
+ *
+ * DESCRIPTIONS
+ *
+ * Reads a byte-swapped CPIO binary archive header
+ *
+ * PARMAMETERS
+ *
+ * char *magic - magic number to match
+ * char *name - name of the file which is stored in the header.
+ * (modified and passed back to caller).
+ * Stat *asb - stat block for the file (modified and passed back
+ * to the caller).
+ *
+ *
+ * RETURNS
+ *
+ * Returns the number of trailing alignment bytes to skip; -1 if
+ * unsuccessful.
+ *
+ */
+
+#ifdef __STDC__
+
+static int inswab(char *magic, char *name, Stat *asb)
+
+#else
+
+static int inswab(magic, name, asb)
+char *magic;
+char *name;
+Stat *asb;
+
+#endif
+{
+ ushort namesize;
+ uint namefull;
+ Binary binary;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static int inswab() in extract.c\n");
+#endif
+ if (*((ushort *) magic) != SWAB(M_BINARY)) {
+ return (-1);
+ }
+ memcpy((char *) &binary,
+ magic + sizeof(ushort),
+ M_STRLEN - sizeof(ushort));
+ if (buf_read((char *) &binary + M_STRLEN - sizeof(ushort),
+ sizeof(binary) - (M_STRLEN - sizeof(ushort))) < 0) {
+ warnarch("Corrupt swapped header",
+ (OFFSET) sizeof(binary) - (M_STRLEN - sizeof(ushort)));
+ return (-1);
+ }
+ asb->sb_dev = (dev_t) SWAB(binary.b_dev);
+ asb->sb_ino = (ino_t) SWAB(binary.b_ino);
+ asb->sb_mode = SWAB(binary.b_mode);
+ asb->sb_uid = SWAB(binary.b_uid);
+ asb->sb_gid = SWAB(binary.b_gid);
+ asb->sb_nlink = SWAB(binary.b_nlink);
+#ifndef _POSIX_SOURCE
+ asb->sb_rdev = (dev_t) SWAB(binary.b_rdev);
+#endif
+ asb->sb_mtime = (time_t) SWAB(binary.b_mtime[0]) << 16 | SWAB(binary.b_mtime[1]); /* Xn */
+ asb->sb_size = (time_t) SWAB(binary.b_size[0]) << 16 | SWAB(binary.b_size[1]); /* Xn */
+ if ((namesize = SWAB(binary.b_name)) == 0 || namesize >= PATH_MAX) {
+ warnarch("Bad swapped pathname length",
+ (OFFSET) sizeof(binary) - (M_STRLEN - sizeof(ushort)));
+ return (-1);
+ }
+ if (buf_read(name, namefull = namesize + namesize % 2) < 0) {
+ warnarch("Corrupt swapped pathname", (OFFSET) namefull);
+ return (-1);
+ }
+ if (name[namesize - 1] != '\0') {
+ warnarch("Bad swapped pathname", (OFFSET) namefull);
+ return (-1);
+ }
+ return (asb->sb_size % 2);
+}
+
+
+/* inascii - read in an ASCII cpio header
+ *
+ * DESCRIPTION
+ *
+ * Reads an ASCII format cpio header
+ *
+ * PARAMETERS
+ *
+ * char *magic - magic number to match
+ * char *name - name of the file which is stored in the header.
+ * (modified and passed back to caller).
+ * Stat *asb - stat block for the file (modified and passed back
+ * to the caller).
+ *
+ * RETURNS
+ *
+ * Returns zero if successful; -1 otherwise. Assumes that the entire
+ * magic number has been read.
+ */
+
+#ifdef __STDC__
+
+static int inascii(char *magic, char *name, Stat *asb)
+
+#else
+
+static int inascii(magic, name, asb)
+char *magic;
+char *name;
+Stat *asb;
+
+#endif
+{
+ uint namelen;
+ char header[H_STRLEN + 1];
+ dev_t unused;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static int inascii() in extract.c\n");
+#endif
+ if (strncmp(magic, M_ASCII, M_STRLEN) != 0) {
+ return (-1);
+ }
+ if (buf_read(header, H_STRLEN) < 0) {
+ warnarch("Corrupt ASCII header", (OFFSET) H_STRLEN);
+ return (-1);
+ }
+ header[H_STRLEN] = '\0';
+ asb->sb_mode = 0;
+ if (sscanf(header, H_SCAN, &asb->sb_dev,
+ &asb->sb_ino, &asb->sb_mode, &asb->sb_uid,
+#ifdef _POSIX_SOURCE
+ &asb->sb_gid, &asb->sb_nlink, &unused,
+#else
+ &asb->sb_gid, &asb->sb_nlink, &asb->sb_rdev,
+#endif
+ &asb->sb_mtime, &namelen, &asb->sb_size) != H_COUNT) {
+ warnarch("Bad ASCII header", (OFFSET) H_STRLEN);
+ return (-1);
+ }
+
+ if (namelen == 0 || namelen >= PATH_MAX) {
+ warnarch("Bad ASCII pathname length", (OFFSET) H_STRLEN);
+ return (-1);
+ }
+ if (buf_read(name, namelen) < 0) {
+ warnarch("Corrupt ASCII pathname", (OFFSET) namelen);
+ return (-1);
+ }
+ if (name[namelen - 1] != '\0') {
+ warnarch("Bad ASCII pathname", (OFFSET) namelen);
+ return (-1);
+ }
+ return (0);
+}
+
+
+/* inbinary - read a binary header
+ *
+ * DESCRIPTION
+ *
+ * Reads a CPIO format binary header.
+ *
+ * PARAMETERS
+ *
+ * char *magic - magic number to match
+ * char *name - name of the file which is stored in the header.
+ * (modified and passed back to caller).
+ * Stat *asb - stat block for the file (modified and passed back
+ * to the caller).
+ *
+ * RETURNS
+ *
+ * Returns the number of trailing alignment bytes to skip; -1 if
+ * unsuccessful.
+ */
+
+#ifdef __STDC__
+
+static int inbinary(char *magic, char *name, Stat *asb)
+
+#else
+
+static int inbinary(magic, name, asb)
+char *magic;
+char *name;
+Stat *asb;
+
+#endif
+{
+ uint namefull;
+ Binary binary;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static int inbinary() in extract.c\n");
+#endif
+ if (*((ushort *) magic) != M_BINARY) {
+ return (-1);
+ }
+ memcpy((char *) &binary,
+ magic + sizeof(ushort),
+ M_STRLEN - sizeof(ushort));
+ if (buf_read((char *) &binary + M_STRLEN - sizeof(ushort),
+ sizeof(binary) - (M_STRLEN - sizeof(ushort))) < 0) {
+ warnarch("Corrupt binary header",
+ (OFFSET) sizeof(binary) - (M_STRLEN - sizeof(ushort)));
+ return (-1);
+ }
+ asb->sb_dev = binary.b_dev;
+ asb->sb_ino = binary.b_ino;
+ asb->sb_mode = binary.b_mode;
+ asb->sb_uid = binary.b_uid;
+ asb->sb_gid = binary.b_gid;
+ asb->sb_nlink = binary.b_nlink;
+#ifndef _POSIX_SOURCE
+ asb->sb_rdev = binary.b_rdev;
+#endif
+ asb->sb_mtime = (time_t) binary.b_mtime[0] << 16 | binary.b_mtime[1]; /* Xn */
+ asb->sb_size = (time_t) binary.b_size[0] << 16 | binary.b_size[1]; /* Xn */
+ if (binary.b_name == 0 || binary.b_name >= PATH_MAX) {
+ warnarch("Bad binary pathname length",
+ (OFFSET) sizeof(binary) - (M_STRLEN - sizeof(ushort)));
+ return (-1);
+ }
+ if (buf_read(name, namefull = binary.b_name + binary.b_name % 2) < 0) {
+ warnarch("Corrupt binary pathname", (OFFSET) namefull);
+ return (-1);
+ }
+ if (name[binary.b_name - 1] != '\0') {
+ warnarch("Bad binary pathname", (OFFSET) namefull);
+ return (-1);
+ }
+ return (asb->sb_size % 2);
+}
diff --git a/private/posix/programs/pax/fileio.c b/private/posix/programs/pax/fileio.c
new file mode 100644
index 000000000..f71de99cd
--- /dev/null
+++ b/private/posix/programs/pax/fileio.c
@@ -0,0 +1,498 @@
+/* $Source: /u/mark/src/pax/RCS/fileio.c,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * fileio.c - file I/O functions for all archive interfaces
+ *
+ * DESCRIPTION
+ *
+ * These function all do I/O of some form or another. They are
+ * grouped here mainly for convienence.
+ *
+ * AUTHOR
+ *
+ * Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
+ *
+ * Sponsored by The USENIX Association for public distribution.
+ *
+ * Copyright (c) 1989 Mark H. Colburn.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice is 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 Mark H. Colburn and sponsored by The
+ * USENIX Association.
+ *
+ * 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.
+ *
+ * $Log: fileio.c,v $
+ * Revision 1.2 89/02/12 10:04:31 mark
+ * 1.2 release fixes
+ *
+ * Revision 1.1 88/12/23 18:02:09 mark
+ * Initial revision
+ *
+ */
+
+#ifndef lint
+static char *ident = "$Id: fileio.c,v 1.2 89/02/12 10:04:31 mark Exp $";
+static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n";
+#endif /* ! lint */
+
+
+/* Headers */
+
+#include "pax.h"
+
+
+/* open_archive - open an archive file.
+ *
+ * DESCRIPTION
+ *
+ * Open_archive will open an archive file for reading or writing,
+ * setting the proper file mode, depending on the "mode" passed to
+ * it. All buffer pointers are reset according to the mode
+ * specified.
+ *
+ * PARAMETERS
+ *
+ * int mode - specifies whether we are reading or writing.
+ *
+ * RETURNS
+ *
+ * Returns a zero if successfull, or -1 if an error occured during
+ * the open.
+ */
+
+#ifdef __STDC__
+
+int open_archive(int mode)
+
+#else
+
+int open_archive(mode)
+int mode;
+
+#endif
+{
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int open_archive() in fileio.c\n");
+#endif
+ if (ar_file[0] == '-' && ar_file[1] == '\0') {
+ if (mode == AR_READ) {
+ archivefd = STDIN;
+ bufend = bufidx = bufstart;
+ } else {
+ archivefd = STDOUT;
+ }
+ } else if (mode == AR_READ) {
+ archivefd = open(ar_file, O_RDONLY | O_BINARY);
+ bufend = bufidx = bufstart; /* set up for initial read */
+ } else if (mode == AR_WRITE) {
+ archivefd = open(ar_file, O_WRONLY|O_TRUNC|O_CREAT|O_BINARY, /* Xn */
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); /* Xn */
+ } else if (mode == AR_APPEND) {
+ archivefd = open(ar_file, O_RDWR | O_BINARY, /* Xn */
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); /* Xn */
+ bufend = bufidx = bufstart; /* set up for initial read */
+ }
+
+ if (archivefd < 0) {
+ warnarch(strerror(errno), (OFFSET) 0); /* Xn */
+ return (-1);
+ }
+ ++arvolume;
+ return (0);
+}
+
+
+/* close_archive - close the archive file
+ *
+ * DESCRIPTION
+ *
+ * Closes the current archive and resets the archive end of file
+ * marker.
+ */
+
+#ifdef __STDC__
+
+void close_archive(void)
+
+#else
+
+void close_archive()
+
+#endif
+{
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: void close_archive() in fileio.c\n");
+#endif
+ if (archivefd != STDIN && archivefd != STDOUT) {
+ close(archivefd);
+ }
+ areof = 0;
+}
+
+
+/* openout - open an output file
+ *
+ * DESCRIPTION
+ *
+ * Openo opens the named file for output. The file mode and type are
+ * set based on the values stored in the stat structure for the file.
+ * If the file is a special file, then no data will be written, the
+ * file/directory/Fifo, etc., will just be created. Appropriate
+ * permission may be required to create special files.
+ *
+ * PARAMETERS
+ *
+ * char *name - The name of the file to create
+ * Stat *asb - Stat structure for the file
+ * Link *linkp; - pointer to link chain for this file
+ * int ispass - true if we are operating in "pass" mode
+ *
+ * RETURNS
+ *
+ * Returns the output file descriptor, 0 if no data is required or -1
+ * if unsuccessful. Note that UNIX open() will never return 0 because
+ * the standard input is in use.
+ */
+
+#ifdef __STDC__
+
+int openout(char *name, Stat *asb, Link *linkp, int ispass)
+
+#else
+
+int openout(name, asb, linkp, ispass)
+char *name;
+Stat *asb;
+Link *linkp;
+int ispass;
+
+#endif
+{
+ int exists;
+ int fd;
+ ushort perm;
+ ushort operm = 0;
+ Stat osb;
+#ifdef S_IFLNK
+ int ssize;
+ char sname[PATH_MAX + 1];
+#endif /* S_IFLNK */
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int openout() in fileio.c\n");
+#endif
+ if (exists = (LSTAT(name, &osb) == 0)) {
+ if (ispass && osb.sb_ino == asb->sb_ino && osb.sb_dev == asb->sb_dev) {
+ warn(name, "Same file");
+ return (-1);
+ } else if ((osb.sb_mode & S_IFMT) == (asb->sb_mode & S_IFMT)) {
+ operm = osb.sb_mode & S_IPERM;
+ } else if (REMOVE(name, &osb) < 0) {
+ warn(name, strerror(errno)); /* Xn */
+ return (-1);
+ } else {
+ exists = 0;
+ }
+ }
+ if (linkp) {
+ if (exists) {
+ if (asb->sb_ino == osb.sb_ino && asb->sb_dev == osb.sb_dev) {
+ return (0);
+ } else if (unlink(name) < 0) {
+ warn(name, strerror(errno)); /* Xn */
+ return (-1);
+ } else {
+ exists = 0;
+ }
+ }
+ if (link(linkp->l_name, name) != 0) {
+ if (errno == ENOENT) {
+ if (f_dir_create) {
+ if (dirneed(name) != 0 ||
+ link(linkp->l_name, name) != 0) {
+ warn(name, strerror(errno)); /* Xn */
+ return (-1);
+ }
+ } else {
+ warn(name,
+ "Directories are not being created (-d option)");
+ }
+ return(0);
+ } else if (errno != EXDEV) {
+ warn(name, strerror(errno)); /* Xn */
+ return (-1);
+ }
+ } else {
+ return(0);
+ }
+ }
+ perm = asb->sb_mode & S_IPERM;
+ switch (asb->sb_mode & S_IFMT) {
+ case S_IFBLK:
+ case S_IFCHR:
+ fd = 0;
+ if (exists) {
+#ifndef _POSIX_SOURCE
+ if (asb->sb_rdev == osb.sb_rdev) {
+ if (perm != operm && chmod(name, (int) perm) < 0) {
+ warn(name, strerror(errno)); /* Xn */
+ return (-1);
+ } else {
+ break;
+ }
+ } else if (REMOVE(name, &osb) < 0) {
+ warn(name, strerror(errno)); /* Xn */
+ return (-1);
+ } else {
+ exists = 0;
+ }
+#else
+ if (REMOVE(name, &osb) < 0) {
+ warn(name, strerror(errno)); /* Xn */
+ return (-1);
+ } else {
+ exists = 0;
+ }
+#endif /* _POSIX_SOURCE */
+ }
+#ifdef _POSIX_SOURCE
+ if (mknod(name, (int) asb->sb_mode, 0))
+#else
+ if (mknod(name, (int) asb->sb_mode, (int) asb->sb_rdev) < 0)
+#endif
+ {
+ if (errno == ENOENT) {
+ if (f_dir_create) {
+ if (dirneed(name) < 0 || mknod(name, (int) asb->sb_mode,
+#ifdef _POSIX_SOURCE
+ 0))
+#else
+ (int) asb->sb_rdev) < 0)
+#endif
+ {
+ warn(name, strerror(errno)); /* Xn */
+ return (-1);
+ }
+ } else {
+ warn(name, "Directories are not being created (-d option)");
+ }
+ } else {
+ warn(name, strerror(errno)); /* Xn */
+ return (-1);
+ }
+ }
+ return(0);
+ break;
+ case S_IFDIR:
+ if (exists) {
+ if (perm != operm && chmod(name, (int) perm) < 0) {
+ warn(name, strerror(errno)); /* Xn */
+ return (-1);
+ }
+ } else if (f_dir_create) {
+ if (dirmake(name, asb) < 0 || dirneed(name) < 0) {
+ warn(name, strerror(errno)); /* Xn */
+ return (-1);
+ }
+ } else {
+ warn(name, "Directories are not being created (-d option)");
+ }
+ return (0);
+#ifdef S_IFIFO
+ case S_IFIFO:
+ fd = 0;
+ if (exists) {
+ if (perm != operm && chmod(name, (int) perm) < 0) {
+ warn(name, strerror(errno)); /* Xn */
+ return (-1);
+ }
+ }
+# ifdef _POSIX_SOURCE /* Xn */
+ else if (mkfifo(name, asb->sb_mode) < 0) /* Xn */
+# else /* Xn */
+ else if (mknod(name, (int) asb->sb_mode, 0) < 0)
+# endif /* Xn */
+ {
+ if (errno == ENOENT) {
+ if (f_dir_create) {
+ if (dirneed(name) < 0
+# ifdef _POSIX_SOURCE /* Xn */
+ || mkfifo(name, asb->sb_mode) < 0) /* Xn */
+# else /* Xn */
+ || mknod(name, (int) asb->sb_mode, 0) < 0)
+# endif /* _POSIX_SOURCE */ /* Xn */
+ {
+ warn(name, strerror(errno)); /* Xn */
+ return (-1);
+ }
+ } else {
+ warn(name, "Directories are not being created (-d option)");
+ }
+ } else {
+ warn(name, strerror(errno)); /* Xn */
+ return (-1);
+ }
+ }
+ return(0);
+ break;
+#endif /* S_IFIFO */
+#ifdef S_IFLNK
+ case S_IFLNK:
+ if (exists) {
+ if ((ssize = readlink(name, sname, sizeof(sname))) < 0) {
+ warn(name, strerror(errno)); /* Xn */
+ return (-1);
+ } else if (strncmp(sname, asb->sb_link, ssize) == 0) {
+ return (0);
+ } else if (REMOVE(name, &osb) < 0) {
+ warn(name, strerror(errno)); /* Xn */
+ return (-1);
+ } else {
+ exists = 0;
+ }
+ }
+ if (symlink(asb->sb_link, name) < 0) {
+ if (errno == ENOENT) {
+ if (f_dir_create) {
+ if (dirneed(name) < 0 || symlink(asb->sb_link, name) < 0) {
+ warn(name, strerror(errno)); /* Xn */
+ return (-1);
+ }
+ } else {
+ warn(name, "Directories are not being created (-d option)");
+ }
+ } else {
+ warn(name, strerror(errno)); /* Xn */
+ return (-1);
+ }
+ }
+ return (0); /* Can't chown()/chmod() a symbolic link */
+#endif /* S_IFLNK */
+ case S_IFREG:
+ if (exists) {
+ if (!f_unconditional && osb.sb_mtime > asb->sb_mtime) {
+ warn(name, "Newer file exists");
+ return (-1);
+ } else if (unlink(name) < 0) {
+ warn(name, strerror(errno)); /* Xn */
+ return (-1);
+ } else {
+ exists = 0;
+ }
+ }
+ if ((fd = creat(name, (int) perm)) < 0) {
+ if (errno == ENOENT) {
+ if (f_dir_create) {
+ if (dirneed(name) < 0 ||
+ (fd = creat(name, (int) perm)) < 0) {
+ warn(name, strerror(errno)); /* Xn */
+ return (-1);
+ }
+ } else {
+ /*
+ * the file requires a directory which does not exist
+ * and which the user does not want created, so skip
+ * the file...
+ */
+ warn(name, "Directories are not being created (-d option)");
+ return(0);
+ }
+ } else {
+ warn(name, strerror(errno)); /* Xn */
+ return (-1);
+ }
+ }
+ break;
+ default:
+ warn(name, "Unknown filetype");
+ return (-1);
+ }
+ if (f_owner) {
+ if (!exists || asb->sb_uid != osb.sb_uid || asb->sb_gid != osb.sb_gid) {
+ chown(name, (int) asb->sb_uid, (int) asb->sb_gid);
+ }
+ }
+ return (fd);
+}
+
+
+/* openin - open the next input file
+ *
+ * DESCRIPTION
+ *
+ * Openi will attempt to open the next file for input. If the file is
+ * a special file, such as a directory, FIFO, link, character- or
+ * block-special file, then the file size field of the stat structure
+ * is zeroed to make sure that no data is written out for the file.
+ * If the file is a special file, then a file descriptor of 0 is
+ * returned to the caller, which is handled specially. If the file
+ * is a regular file, then the file is opened and a file descriptor
+ * to the open file is returned to the caller.
+ *
+ * PARAMETERS
+ *
+ * char *name - pointer to the name of the file to open
+ * Stat *asb - pointer to the stat block for the file to open
+ *
+ * RETURNS
+ *
+ * Returns a file descriptor, 0 if no data exists, or -1 at EOF. This
+ * kludge works because standard input is in use, preventing open() from
+ * returning zero.
+ */
+
+#ifdef __STDC__
+
+int openin(char *name, Stat *asb)
+
+#else
+
+int openin(name, asb)
+char *name; /* name of file to open */
+Stat *asb; /* pointer to stat structure for file */
+
+#endif
+{
+ int fd;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int openin() in fileio.c\n");
+#endif
+ switch (asb->sb_mode & S_IFMT) {
+ case S_IFDIR:
+#if 0 /* NIST-PCTS */
+ asb->sb_nlink = 1;
+#endif /* NIST-PCTS */
+ asb->sb_size = 0;
+ return (0);
+#ifdef S_IFLNK
+ case S_IFLNK:
+ if ((asb->sb_size = readlink(name,
+ asb->sb_link, sizeof(asb->sb_link) - 1)) < 0) {
+ warn(name, strerror(errno)); /* Xn */
+ return(0);
+ }
+ asb->sb_link[asb->sb_size] = '\0';
+ return (0);
+#endif /* S_IFLNK */
+ case S_IFREG:
+ if (asb->sb_size == 0) {
+ return (0);
+ }
+ if ((fd = open(name, O_RDONLY | O_BINARY)) < 0) {
+ warn(name, strerror(errno)); /* Xn */
+ }
+ return (fd);
+ default:
+ asb->sb_size = 0;
+ return (0);
+ }
+}
diff --git a/private/posix/programs/pax/func.h b/private/posix/programs/pax/func.h
new file mode 100644
index 000000000..007f30484
--- /dev/null
+++ b/private/posix/programs/pax/func.h
@@ -0,0 +1,199 @@
+/* $Source: /u/mark/src/pax/RCS/func.h,v $
+ *
+ * $Revision: 1.3 $
+ *
+ * func.h - function type and argument declarations
+ *
+ * DESCRIPTION
+ *
+ * This file contains function delcarations in both ANSI style
+ * (function prototypes) and traditional style.
+ *
+ * AUTHOR
+ *
+ * Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
+ *
+ * Sponsored by The USENIX Association for public distribution.
+ *
+ * Copyright (c) 1989 Mark H. Colburn.
+ * 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 Mark H. Colburn and sponsored by The USENIX Association.
+ *
+ * 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 _PAX_FUNC_H
+#define _PAX_FUNC_H
+
+/* Function Prototypes */
+
+#ifdef __STDC__
+
+extern Link *linkfrom(char *, Stat *);
+extern Link *linkto(char *, Stat *);
+extern char *mem_get(uint);
+extern char *mem_str(char *);
+extern char *strerror(int); /* Xn */
+extern int ar_read(void);
+extern int buf_read(char *, uint);
+extern int buf_skip(OFFSET);
+extern int create_archive(void);
+extern int dirneed(char *);
+extern void read_archive(void); /* Xn */
+extern int inentry(char *, Stat *);
+extern int lineget(FILE *, char *);
+extern int name_match(char *);
+extern int name_next(char *, Stat *);
+extern int nameopt(char *);
+extern int open_archive(int);
+extern int open_tty(void);
+extern int openin(char *, Stat *);
+extern int openout(char *, Stat *, Link *, int);
+extern void pass(char *); /* Xn */
+extern int passitem(char *, Stat *, int, char *);
+extern int read_header(char *, Stat *);
+extern int wildmat(char *, char *);
+extern void buf_allocate(OFFSET);
+extern void close_archive(void);
+extern void fatal(char *);
+extern void name_gather(void);
+extern void name_init(int, char **);
+extern void names_notfound(void);
+extern void next(int);
+extern int nextask(char *, char *, int);
+extern void outdata(int, char *, OFFSET);
+extern void outwrite(char *, uint);
+extern void passdata(char *, int, char *, int);
+extern void print_entry(char *, Stat *);
+extern void warn(char *, char *); /* Xn */
+extern void warnarch(char *, OFFSET);
+extern void write_eot(void);
+extern void get_archive_type(void);
+extern char *getenv(const char *);
+extern void *malloc(size_t); /* Xn */
+extern char *strcat(char *, const char *); /* Xn */
+extern char *strcpy(char *, const char *); /* Xn */
+extern char *strncpy(char *, const char *, size_t); /* Xn */
+extern SIG_T (*signal(int, SIG_T (*)(int)))(int); /* Xn */
+extern OFFSET lseek(int, OFFSET, int); /* Xn */
+extern struct group *getgrgid(gid_t); /* Xn */
+extern struct group *getgrnam(const char *); /* Xn */
+extern struct passwd *getpwuid(uid_t); /* Xn */
+extern struct tm *localtime(const time_t *); /* Xn */
+extern time_t time(time_t *); /* Xn */
+extern uint sleep(uint); /* Xn */
+extern void _exit(int); /* Xn */
+extern void exit(int); /* Xn */
+extern void free(void *); /* Xn */
+extern Link *islink(char *, Stat *);
+extern char *finduname(int);
+extern char *findgname(int);
+extern int findgid(char *); /* Xn */
+extern int get_header(char *, Stat *); /* Xn */
+extern int getopt(int argc, const char *const argv[], const char *optstring); /* Xn */
+extern void rpl_name(char *); /* Xn */
+extern int get_disposition(char *, char *); /* Xn */
+extern int get_newname(char *, int); /* Xn */
+# ifdef _POSIX_SOURCE /* Xn */
+extern int mkfifo(const char *, mode_t); /* Xn */
+# else /* Xn */
+extern int mknod(char *, int, ...); /* Xn */
+# endif /* Xn */
+extern int dirmake(char *, Stat *); /* Xn */
+extern int finduid(char *); /* Xn */
+extern void do_tar(int, char **); /* Xn */
+extern void do_cpio(int, char **); /* Xn */
+extern void add_replstr(char *); /* Xn */
+extern void append_archive(void); /* Xn */
+extern void linkleft(void); /* Xn */
+
+#else /* !__STDC__ */
+
+extern Link *linkfrom();
+extern Link *linkto();
+extern char *mem_get();
+extern char *mem_str();
+extern char *strerror();
+extern int ar_read();
+extern int buf_read();
+extern int buf_skip();
+extern int create_archive();
+extern int dirneed();
+extern void read_archive(); /* Xn */
+extern int inentry();
+extern int lineget();
+extern int name_match();
+extern int name_next();
+extern int nameopt();
+extern int open_archive();
+extern int open_tty();
+extern int openin();
+extern int openout();
+extern void pass(); /* Xn */
+extern int passitem();
+extern int read_header();
+extern int wildmat();
+extern void buf_allocate();
+extern void close_archive();
+extern void fatal();
+extern void name_gather();
+extern void name_init();
+extern void names_notfound();
+extern void next();
+extern int nextask();
+extern void outdata();
+extern void outwrite();
+extern void passdata();
+extern void print_entry();
+extern void warn();
+extern void warnarch();
+extern void write_eot();
+extern void get_archive_type();
+extern char *getenv();
+extern char *malloc();
+extern char *strcat();
+extern char *strcpy();
+extern char *strncpy();
+extern SIG_T (*signal())();
+extern OFFSET lseek();
+extern struct group *getgrgid();
+extern struct group *getgrnam();
+extern struct passwd *getpwuid();
+extern struct tm *localtime();
+extern time_t time();
+extern uint sleep();
+extern void _exit();
+extern void exit();
+extern void free();
+extern Link *islink();
+extern char *finduname();
+extern char *findgname();
+extern int findgid();
+extern int get_header(); /* Xn */
+extern int getopt(); /* Xn */
+extern void rpl_name(); /* Xn */
+extern int get_disposition(); /* Xn */
+extern int get_newname(); /* Xn */
+# ifdef _POSIX_SOURCE /* Xn */
+extern int mkfifo(); /* Xn */
+# else /* Xn */
+extern int mknod(); /* Xn */
+# endif /* Xn */
+extern int dirmake(); /* Xn */
+extern int finduid(); /* Xn */
+extern void do_tar(); /* Xn */
+extern void do_cpio(); /* Xn */
+extern void add_replstr(); /* Xn */
+extern void append_archive(); /* Xn */
+extern void linkleft(); /* Xn */
+
+#endif /* __STDC__ */
+#endif /* _PAX_FUNC_H */
diff --git a/private/posix/programs/pax/getopt.c b/private/posix/programs/pax/getopt.c
new file mode 100644
index 000000000..a19d9ba2c
--- /dev/null
+++ b/private/posix/programs/pax/getopt.c
@@ -0,0 +1,108 @@
+/*
+ getopt.c
+
+ modified public-domain AT&T getopt(3)
+*/
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef _POSIX_SOURCE
+# include <unistd.h>
+#else
+# define STDERR_FILENO 2
+# ifdef __STDC__
+ extern int write (int fildes, char * buf, unsigned nbyte);
+# else
+ extern int write ();
+# endif
+#endif
+
+int opterr = 1;
+int optind = 1;
+int optopt;
+char * optarg;
+
+#ifdef __STDC__
+ static void ERR (char ** argv, char * s, char c)
+#else
+ static void ERR (argv, s, c)
+ char ** argv, * s, c;
+#endif
+{
+ char errbuf[2];
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static void ERR () in getopt.c\n");
+#endif
+ if (opterr)
+ {
+ errbuf[0] = c;
+ errbuf[1] = '\n';
+ (void) write(STDERR_FILENO,argv[0],strlen(argv[0]));
+ (void) write(STDERR_FILENO,s,strlen(s));
+ (void) write(STDERR_FILENO,errbuf,sizeof errbuf);
+ }
+}
+
+#ifdef __STDC__
+ int getopt (int argc, char ** argv, char * opts)
+#else
+ int getopt (argc, argv, opts)
+ int argc;
+ char ** argv, * opts;
+#endif
+{
+ static int sp = 1, error = (int) '?';
+ static char sw = '-', eos = '\0', arg = ':';
+ register char c, * cp;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int getopt () in getopt.c\n");
+#endif
+ if (sp == 1)
+ if (optind >= argc || argv[optind][0] != sw
+ || argv[optind][1] == eos)
+ return EOF;
+ else if (strcmp(argv[optind],"--") == 0)
+ {
+ optind++;
+ return EOF;
+ }
+ c = argv[optind][sp];
+ optopt = (int) c;
+ if (c == arg || (cp = strchr(opts,c)) == NULL)
+ {
+ ERR(argv,": illegal option--",c);
+ if (argv[optind][++sp] == eos)
+ {
+ optind++;
+ sp = 1;
+ }
+ return error;
+ }
+ else if (*++cp == arg)
+ {
+ if (argv[optind][sp + 1] != eos)
+ optarg = &argv[optind++][sp + 1];
+ else if (++optind >= argc)
+ {
+ ERR(argv,": option requires an argument--",c);
+ sp = 1;
+ return error;
+ }
+ else
+ optarg = argv[optind++];
+ sp = 1;
+ }
+ else
+ {
+ if (argv[optind][++sp] == eos)
+ {
+ sp = 1;
+ optind++;
+ }
+ optarg = NULL;
+ }
+ return (int) c;
+}
diff --git a/private/posix/programs/pax/link.c b/private/posix/programs/pax/link.c
new file mode 100644
index 000000000..2ef4dc506
--- /dev/null
+++ b/private/posix/programs/pax/link.c
@@ -0,0 +1,335 @@
+/* $Source: /u/mark/src/pax/RCS/link.c,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * link.c - functions for handling multiple file links
+ *
+ * DESCRIPTION
+ *
+ * These function manage the link chains which are used to keep track
+ * of outstanding links during archive reading and writing.
+ *
+ * AUTHOR
+ *
+ * Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
+ *
+ * Sponsored by The USENIX Association for public distribution.
+ *
+ * Copyright (c) 1989 Mark H. Colburn.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice is 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 Mark H. Colburn and sponsored by The
+ * USENIX Association.
+ *
+ * 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.
+ *
+ * $Log: link.c,v $
+ * Revision 1.2 89/02/12 10:04:38 mark
+ * 1.2 release fixes
+ *
+ * Revision 1.1 88/12/23 18:02:12 mark
+ * Initial revision
+ *
+ */
+
+#ifndef lint
+static char *ident = "$Id: link.c,v 1.2 89/02/12 10:04:38 mark Exp $";
+static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n";
+#endif /* ! lint */
+
+
+/* Headers */
+
+#include "pax.h"
+
+
+/* Defines */
+
+/*
+ * Address link information base.
+ */
+#define LINKHASH(ino) (linkbase + (ino) % NEL(linkbase))
+
+/*
+ * Number of array elements.
+ */
+#define NEL(a) (sizeof(a) / sizeof(*(a)))
+
+
+
+/* Internal Identifiers */
+
+static Link *linkbase[256]; /* Unresolved link information */
+
+
+/* linkfrom - find a file to link from
+ *
+ * DESCRIPTION
+ *
+ * Linkfrom searches the link chain to see if there is a file in the
+ * link chain which has the same inode number as the file specified
+ * by the stat block pointed at by asb. If a file is found, the
+ * name is returned to the caller, otherwise a NULL is returned.
+ *
+ * PARAMETERS
+ *
+ * char *name - name of the file which we are attempting
+ * to find a link for
+ * Stat *asb - stat structure of file to find a link to
+ *
+ * RETURNS
+ *
+ * Returns a pointer to a link structure, or NULL if unsuccessful.
+ *
+ */
+
+#ifdef __STDC__
+
+Link *linkfrom(char *name, Stat *asb)
+
+#else
+
+Link *linkfrom(name, asb)
+char *name;
+Stat *asb;
+
+#endif
+{
+ Link *linkp;
+ Link *linknext;
+ Path *path;
+ Path *pathnext;
+ Link **abase;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: Link *linkfrom() in link.c\n");
+#endif
+ for (linkp = *(abase = LINKHASH(asb->sb_ino)); linkp; linkp = linknext) {
+ if (linkp->l_nlink == 0) {
+ if (linkp->l_name) {
+ free((char *) linkp->l_name);
+ }
+ if (linknext = linkp->l_forw) {
+ linknext->l_back = linkp->l_back;
+ }
+ if (linkp->l_back) {
+ linkp->l_back->l_forw = linkp->l_forw;
+ }
+ free((char *) linkp);
+ *abase = (Link *)NULL;
+ } else if (linkp->l_ino == asb->sb_ino && linkp->l_dev == asb->sb_dev) {
+ /*
+ * check to see if a file with the name "name" exists in the
+ * chain of files which we have for this particular link
+ */
+ for (path = linkp->l_path; path; path = pathnext) {
+ if (strcmp(path->p_name, name) == 0) {
+ --linkp->l_nlink;
+ if (path->p_name) {
+ free(path->p_name);
+ }
+ if (pathnext = path->p_forw) {
+ pathnext->p_back = path->p_back;
+ }
+ if (path->p_back) {
+ path->p_back->p_forw = pathnext;
+ }
+ if (linkp->l_path == path) {
+ linkp->l_path = pathnext;
+ }
+ free(path);
+ return (linkp);
+ }
+ pathnext = path->p_forw;
+ }
+ return((Link *)NULL);
+ } else {
+ linknext = linkp->l_forw;
+ }
+ }
+ return ((Link *)NULL);
+}
+
+
+
+/* islink - determine whether a given file really a link
+ *
+ * DESCRIPTION
+ *
+ * Islink searches the link chain to see if there is a file in the
+ * link chain which has the same inode number as the file specified
+ * by the stat block pointed at by asb. If a file is found, a
+ * non-zero value is returned to the caller, otherwise a 0 is
+ * returned.
+ *
+ * PARAMETERS
+ *
+ * char *name - name of file to check to see if it is link.
+ * Stat *asb - stat structure of file to find a link to
+ *
+ * RETURNS
+ *
+ * Returns a pointer to a link structure, or NULL if unsuccessful.
+ *
+ */
+
+#ifdef __STDC__
+
+Link *islink(char *name, Stat *asb)
+
+#else
+
+Link *islink(name, asb)
+char *name;
+Stat *asb;
+
+#endif
+{
+ Link *linkp;
+ Link *linknext;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: Link *islink() in link.c\n");
+#endif
+ for (linkp = *(LINKHASH(asb->sb_ino)); linkp; linkp = linknext) {
+ if (linkp->l_ino == asb->sb_ino && linkp->l_dev == asb->sb_dev) {
+ if (strcmp(name, linkp->l_name) == 0) {
+ return ((Link *)NULL);
+ }
+ return (linkp);
+ } else {
+ linknext = linkp->l_forw;
+ }
+ }
+ return ((Link *)NULL);
+}
+
+
+/* linkto - remember a file with outstanding links
+ *
+ * DESCRIPTION
+ *
+ * Linkto adds the specified file to the link chain. Any subsequent
+ * calls to linkfrom which have the same inode will match the file
+ * just entered. If not enough space is available to make the link
+ * then the item is not added to the link chain, and a NULL is
+ * returned to the calling function.
+ *
+ * PARAMETERS
+ *
+ * char *name - name of file to remember
+ * Stat *asb - pointer to stat structure of file to remember
+ *
+ * RETURNS
+ *
+ * Returns a pointer to the associated link structure, or NULL when
+ * linking is not possible.
+ *
+ */
+
+#ifdef __STDC__
+
+Link *linkto(char *name, Stat *asb)
+
+#else
+
+Link *linkto(name, asb)
+char *name;
+Stat *asb;
+
+#endif
+{
+ Link *linkp;
+ Link *linknext;
+ Path *path;
+ Link **abase;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: Link *linkto() in link.c\n");
+#endif
+ for (linkp = *(LINKHASH(asb->sb_ino)); linkp; linkp = linknext) {
+ if (linkp->l_ino == asb->sb_ino && linkp->l_dev == asb->sb_dev) {
+ if ((path = (Path *) mem_get(sizeof(Path))) == (Path *)NULL ||
+ (path->p_name = mem_str(name)) == (char *)NULL) {
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: () in link.c\n");
+#endif
+ return((Link *)NULL);
+ }
+ if (path->p_forw = linkp->l_path) {
+ if (linkp->l_path->p_forw) {
+ linkp->l_path->p_forw->p_back = path;
+ }
+ } else {
+ linkp->l_path = path;
+ }
+ path->p_back = (Path *)NULL;
+ return(linkp);
+ } else {
+ linknext = linkp->l_forw;
+ }
+ }
+ /*
+ * This is a brand new link, for which there is no other information
+ */
+
+ if ((asb->sb_mode & S_IFMT) == S_IFDIR
+ || (linkp = (Link *) mem_get(sizeof(Link))) == (Link *)NULL
+ || (linkp->l_name = mem_str(name)) == (char *)NULL) {
+ return ((Link *)NULL);
+ }
+ linkp->l_dev = asb->sb_dev;
+ linkp->l_ino = asb->sb_ino;
+ linkp->l_nlink = asb->sb_nlink - 1;
+ linkp->l_size = asb->sb_size;
+ linkp->l_path = (Path *)NULL;
+ if (linkp->l_forw = *(abase = LINKHASH(asb->sb_ino))) {
+ linkp->l_forw->l_back = linkp;
+ } else {
+ *abase = linkp;
+ }
+ linkp->l_back = (Link *)NULL;
+ return (linkp);
+}
+
+
+/* linkleft - complain about files with unseen links
+ *
+ * DESCRIPTION
+ *
+ * Linksleft scans through the link chain to see if there were any
+ * files which have outstanding links that were not processed by the
+ * archive. For each file in the link chain for which there was not
+ * a file, and error message is printed.
+ */
+
+#ifdef __STDC__
+
+void linkleft(void)
+
+#else
+
+void linkleft()
+
+#endif
+{
+ Link *lp;
+ Link **base;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: void linkleft() in link.c\n");
+#endif
+ for (base = linkbase; base < linkbase + NEL(linkbase); ++base) {
+ for (lp = *base; lp; lp = lp->l_forw) {
+ if (lp->l_nlink) {
+ warn(lp->l_path->p_name, "Unseen link(s)");
+ }
+ }
+ }
+}
diff --git a/private/posix/programs/pax/list.c b/private/posix/programs/pax/list.c
new file mode 100644
index 000000000..17d8cb41a
--- /dev/null
+++ b/private/posix/programs/pax/list.c
@@ -0,0 +1,714 @@
+/* $Source: /u/mark/src/pax/RCS/list.c,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * list.c - List all files on an archive
+ *
+ * DESCRIPTION
+ *
+ * These function are needed to support archive table of contents and
+ * verbose mode during extraction and creation of achives.
+ *
+ * AUTHOR
+ *
+ * Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
+ *
+ * Sponsored by The USENIX Association for public distribution.
+ *
+ * Copyright (c) 1989 Mark H. Colburn.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice is 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 Mark H. Colburn and sponsored by The
+ * USENIX Association.
+ *
+ * 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.
+ *
+ * $Log: list.c,v $
+ * Revision 1.2 89/02/12 10:04:43 mark
+ * 1.2 release fixes
+ *
+ * Revision 1.1 88/12/23 18:02:14 mark
+ * Initial revision
+ *
+ */
+
+#ifndef lint
+static char *ident = "$Id: list.c,v 1.2 89/02/12 10:04:43 mark Exp $";
+static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n";
+#endif /* ! lint */
+
+
+/* Headers */
+
+#include "pax.h"
+
+
+/* Defines */
+
+/*
+ * isodigit returns non zero iff argument is an octal digit, zero otherwise
+ */
+#define ISODIGIT(c) (((c) >= '0') && ((c) <= '7'))
+
+
+/* Function Prototypes */
+
+#ifdef __STDC__
+
+static void cpio_entry(char *, Stat *);
+static void tar_entry(char *, Stat *);
+static void pax_entry(char *, Stat *);
+static void print_mode(ushort);
+static long from_oct(int digs, char *where);
+
+#else /* !__STDC__ */
+
+static void cpio_entry();
+static void tar_entry();
+static void pax_entry();
+static void print_mode();
+static long from_oct();
+
+#endif /* __STDC__ */
+
+
+/* Internal Identifiers */
+
+static char *monnames[] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+
+/* read_header - read a header record
+ *
+ * DESCRIPTION
+ *
+ * Read a record that's supposed to be a header record. Return its
+ * address in "head", and if it is good, the file's size in
+ * asb->sb_size. Decode things from a file header record into a "Stat".
+ * Also set "head_standard" to !=0 or ==0 depending whether header record
+ * is "Unix Standard" tar format or regular old tar format.
+ *
+ * PARAMETERS
+ *
+ * char *name - pointer which will contain name of file
+ * Stat *asb - pointer which will contain stat info
+ *
+ * RETURNS
+ *
+ * Return 1 for success, 0 if the checksum is bad, EOF on eof, 2 for a
+ * record full of zeros (EOF marker).
+ */
+
+#ifdef __STDC__
+
+int read_header(char *name, Stat *asb)
+
+#else
+
+int read_header(name, asb)
+char *name;
+Stat *asb;
+
+#endif
+{
+ int i;
+ long sum;
+ long recsum;
+#if 0 /* Xn */
+ Link *link;
+#endif /* Xn */
+ char *p;
+ char hdrbuf[BLOCKSIZE];
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int read_header() in list.c\n");
+#endif
+ memset((char *)asb, 0, sizeof(Stat));
+ /* read the header from the buffer */
+ if (buf_read(hdrbuf, BLOCKSIZE) != 0) {
+ return (EOF);
+ }
+
+//XXX.mjb: this code doesn't seem to handle filename which are
+//not null-terminated.
+
+ if ('\0' == hdrbuf[345]) {
+ // there's no prefix for the file
+ strcpy(name, &hdrbuf[0]);
+ } else {
+ strcpy(name, &hdrbuf[345]);
+ strcat(name, "/");
+ strcat(name, &hdrbuf[0]);
+ }
+
+
+ recsum = from_oct(8, &hdrbuf[148]);
+ sum = 0;
+ p = hdrbuf;
+ for (i = 0 ; i < 500; i++) {
+
+ /*
+ * We can't use unsigned char here because of old compilers, e.g. V7.
+ */
+ sum += 0xFF & *p++;
+ }
+
+ /* Adjust checksum to count the "chksum" field as blanks. */
+ for (i = 0; i < 8; i++) {
+ sum -= 0xFF & hdrbuf[148 + i];
+ }
+ sum += ' ' * 8;
+
+ if (sum == 8 * ' ') {
+
+ /*
+ * This is a zeroed record...whole record is 0's except for the 8
+ * blanks we faked for the checksum field.
+ */
+ return (2);
+ }
+ if (sum == recsum) {
+ /*
+ * Good record. Decode file size and return.
+ */
+ if (hdrbuf[156] != LNKTYPE) {
+ asb->sb_size = from_oct(1 + 12, &hdrbuf[124]);
+ }
+ asb->sb_mtime = from_oct(1 + 12, &hdrbuf[136]);
+ asb->sb_mode = (from_oct(8, &hdrbuf[100]) & 0777);
+
+ if (strcmp(&hdrbuf[257], TMAGIC) == 0) {
+ /* Unix Standard tar archive */
+ head_standard = 1;
+#ifdef NONAMES
+ asb->sb_uid = from_oct(8, &hdrbuf[108]);
+ asb->sb_gid = from_oct(8, &hdrbuf[116]);
+#else
+ asb->sb_uid = finduid(&hdrbuf[265]);
+ asb->sb_gid = findgid(&hdrbuf[297]);
+#endif
+ switch (hdrbuf[156]) {
+ case BLKTYPE:
+ case CHRTYPE:
+#ifndef _POSIX_SOURCE
+ asb->sb_rdev = makedev(from_oct(8, &hdrbuf[329]),
+ from_oct(8, &hdrbuf[337]));
+#endif
+ break;
+ default:
+ /* do nothing... */
+ break;
+ }
+ } else {
+ /* Old fashioned tar archive */
+ head_standard = 0;
+ asb->sb_uid = from_oct(8, &hdrbuf[108]);
+ asb->sb_gid = from_oct(8, &hdrbuf[116]);
+ }
+
+ switch (hdrbuf[156]) {
+ case REGTYPE:
+ case AREGTYPE:
+ /*
+ * Berkeley tar stores directories as regular files with a
+ * trailing /
+ */
+ if (name[strlen(name) - 1] == '/') {
+ name[strlen(name) - 1] = '\0';
+ asb->sb_mode |= S_IFDIR;
+ } else {
+ asb->sb_mode |= S_IFREG;
+ }
+ break;
+ case LNKTYPE:
+ asb->sb_nlink = 2;
+ linkto(&hdrbuf[157], asb);
+ linkto(name, asb);
+ asb->sb_mode |= S_IFREG;
+ break;
+ case BLKTYPE:
+ asb->sb_mode |= S_IFBLK;
+ break;
+ case CHRTYPE:
+ asb->sb_mode |= S_IFCHR;
+ break;
+ case DIRTYPE:
+ asb->sb_mode |= S_IFDIR;
+ break;
+#ifdef S_IFLNK
+ case SYMTYPE:
+ asb->sb_mode |= S_IFLNK;
+ strcpy(asb->sb_link, &hdrbuf[157]);
+ break;
+#endif
+#ifdef S_IFIFO
+ case FIFOTYPE:
+ asb->sb_mode |= S_IFIFO;
+ break;
+#endif
+#ifdef S_IFCTG
+ case CONTTYPE:
+ asb->sb_mode |= S_IFCTG;
+ break;
+#endif
+#ifdef S_IFSOCK /* Xn */
+ case SOCKTYPE: /* Xn */
+ asb->sb_mode |= S_IFSOCK; /* Xn */
+ break; /* Xn */
+#endif /* Xn */
+ }
+ return (1);
+ }
+ return (0);
+}
+
+
+/* print_entry - print a single table-of-contents entry
+ *
+ * DESCRIPTION
+ *
+ * Print_entry prints a single line of file information. The format
+ * of the line is the same as that used by the LS command. For some
+ * archive formats, various fields may not make any sense, such as
+ * the link count on tar archives. No error checking is done for bad
+ * or invalid data.
+ *
+ * PARAMETERS
+ *
+ * char *name - pointer to name to print an entry for
+ * Stat *asb - pointer to the stat structure for the file
+ */
+
+#ifdef __STDC__
+
+void print_entry(char *name, Stat *asb)
+
+#else
+
+void print_entry(name, asb)
+char *name;
+Stat *asb;
+
+#endif
+{
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: void print_entry() in list.c\n");
+#endif
+ switch (ar_interface) {
+ case TAR:
+ tar_entry(name, asb);
+ break;
+ case CPIO:
+ cpio_entry(name, asb);
+ break;
+ case PAX:
+ pax_entry(name, asb);
+ break;
+ }
+}
+
+
+/* cpio_entry - print a verbose cpio-style entry
+ *
+ * DESCRIPTION
+ *
+ * Print_entry prints a single line of file information. The format
+ * of the line is the same as that used by the traditional cpio
+ * command. No error checking is done for bad or invalid data.
+ *
+ * PARAMETERS
+ *
+ * char *name - pointer to name to print an entry for
+ * Stat *asb - pointer to the stat structure for the file
+ */
+
+#ifdef __STDC__
+
+static void cpio_entry(char *name, Stat *asb)
+
+#else
+
+static void cpio_entry(name, asb)
+char *name;
+Stat *asb;
+
+#endif
+{
+ struct tm *atm;
+ Link *from;
+ struct passwd *pwp;
+#if 0 /* Xn */
+ struct group *grp;
+#endif /* Xn */
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static void cpio_entry() in list.c\n");
+#endif
+ if (f_list && f_verbose) {
+ fprintf(msgfile, "%-7o", asb->sb_mode);
+ atm = localtime(&asb->sb_mtime);
+ if (pwp = getpwuid((int) USH(asb->sb_uid))) {
+ fprintf(msgfile, "%-6s", pwp->pw_name);
+ } else {
+ fprintf(msgfile, "%-6u", USH(asb->sb_uid));
+ }
+ fprintf(msgfile,"%7ld %3s %2d %02d:%02d:%02d %4d ",
+ asb->sb_size, monnames[atm->tm_mon],
+ atm->tm_mday, atm->tm_hour, atm->tm_min,
+ atm->tm_sec, atm->tm_year + 1900);
+ }
+ fprintf(msgfile, "%s", name);
+ if ((asb->sb_nlink > 1) && (from = islink(name, asb))) {
+ fprintf(msgfile, " linked to %s", from->l_name);
+ }
+#ifdef S_IFLNK
+ if ((asb->sb_mode & S_IFMT) == S_IFLNK) {
+ fprintf(msgfile, " symbolic link to %s", asb->sb_link);
+ }
+#endif /* S_IFLNK */
+ putc('\n', msgfile);
+}
+
+
+/* tar_entry - print a tar verbose mode entry
+ *
+ * DESCRIPTION
+ *
+ * Print_entry prints a single line of tar file information. The format
+ * of the line is the same as that produced by the traditional tar
+ * command. No error checking is done for bad or invalid data.
+ *
+ * PARAMETERS
+ *
+ * char *name - pointer to name to print an entry for
+ * Stat *asb - pointer to the stat structure for the file
+ */
+
+#ifdef __STDC__
+
+static void tar_entry(char *name, Stat *asb)
+
+#else
+
+static void tar_entry(name, asb)
+char *name;
+Stat *asb;
+
+#endif
+{
+ struct tm *atm;
+ int i;
+ int mode;
+ char *symnam = "NULL";
+ Link *link;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static void tar_entry() in list.c\n");
+#endif
+ if ((mode = asb->sb_mode & S_IFMT) == S_IFDIR) {
+ return; /* don't print directories */
+ }
+ if (f_extract) {
+ switch (mode) {
+#ifdef S_IFLNK
+ case S_IFLNK: /* This file is a symbolic link */
+ i = readlink(name, symnam, PATH_MAX - 1);
+ if (i < 0) { /* Could not find symbolic link */
+ warn("can't read symbolic link", strerror(errno)); /* Xn */
+ } else { /* Found symbolic link filename */
+ symnam[i] = '\0';
+ fprintf(msgfile, "x %s symbolic link to %s\n", name, symnam);
+ }
+ break;
+#endif
+ case S_IFREG: /* It is a link or a file */
+ if ((asb->sb_nlink > 1) && (link = islink(name, asb))) {
+ fprintf(msgfile, "%s linked to %s\n", name, link->l_name);
+ } else {
+ fprintf(msgfile, "x %s, %ld bytes, %d tape blocks\n",
+ name, asb->sb_size, ROUNDUP(asb->sb_size,
+ BLOCKSIZE) / BLOCKSIZE);
+ }
+ }
+ } else if (f_append || f_create) {
+ switch (mode) {
+#ifdef S_IFLNK
+ case S_IFLNK: /* This file is a symbolic link */
+ i = readlink(name, symnam, PATH_MAX - 1);
+ if (i < 0) { /* Could not find symbolic link */
+ warn("can't read symbolic link", strerror(errno)); /* Xn */
+ } else { /* Found symbolic link filename */
+ symnam[i] = '\0';
+ fprintf(msgfile, "a %s symbolic link to %s\n", name, symnam);
+ }
+ break;
+#endif
+ case S_IFREG: /* It is a link or a file */
+ fprintf(msgfile, "a %s ", name);
+ if ((asb->sb_nlink > 1) && (link = islink(name, asb))) {
+ fprintf(msgfile, "link to %s\n", link->l_name);
+ } else {
+ fprintf(msgfile, "%ld Blocks\n",
+ ROUNDUP(asb->sb_size, BLOCKSIZE) / BLOCKSIZE);
+ }
+ break;
+ }
+ } else if (f_list) {
+ if (f_verbose) {
+ atm = localtime(&asb->sb_mtime);
+ print_mode(asb->sb_mode);
+ fprintf(msgfile," %d/%d %6d %3s %2d %02d:%02d %4d %s",
+ asb->sb_uid, asb->sb_gid, asb->sb_size,
+ monnames[atm->tm_mon], atm->tm_mday, atm->tm_hour,
+ atm->tm_min, atm->tm_year + 1900, name);
+ } else {
+ fprintf(msgfile, "%s", name);
+ }
+ switch (mode) {
+#ifdef S_IFLNK
+ case S_IFLNK: /* This file is a symbolic link */
+ i = readlink(name, symnam, PATH_MAX - 1);
+ if (i < 0) { /* Could not find symbolic link */
+ warn("can't read symbolic link", strerror(errno)); /* Xn */
+ } else { /* Found symbolic link filename */
+ symnam[i] = '\0';
+ fprintf(msgfile, " symbolic link to %s", symnam);
+ }
+ break;
+#endif
+ case S_IFREG: /* It is a link or a file */
+ if ((asb->sb_nlink > 1) && (link = islink(name, asb))) {
+ fprintf(msgfile, " linked to %s", link->l_name);
+ }
+ break; /* Do not print out directories */
+ }
+ fputc('\n', msgfile);
+ } else {
+ fprintf(msgfile, "? %s %ld blocks\n", name,
+ ROUNDUP(asb->sb_size, BLOCKSIZE) / BLOCKSIZE);
+ }
+}
+
+
+/* pax_entry - print a verbose cpio-style entry
+ *
+ * DESCRIPTION
+ *
+ * Print_entry prints a single line of file information. The format
+ * of the line is the same as that used by the LS command.
+ * No error checking is done for bad or invalid data.
+ *
+ * PARAMETERS
+ *
+ * char *name - pointer to name to print an entry for
+ * Stat *asb - pointer to the stat structure for the file
+ */
+
+#ifdef __STDC__
+
+static void pax_entry(char *name, Stat *asb)
+
+#else
+
+static void pax_entry(name, asb)
+char *name;
+Stat *asb;
+
+#endif
+{
+ struct tm *atm;
+ Link *from;
+ struct passwd *pwp;
+ struct group *grp;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static void pax_entry() in list.c\n");
+#endif
+ if (f_list && f_verbose) {
+ print_mode(asb->sb_mode);
+ fprintf(msgfile, " %2d", asb->sb_nlink);
+ atm = localtime(&asb->sb_mtime);
+ if (pwp = getpwuid((int) USH(asb->sb_uid))) {
+ fprintf(msgfile, " %-8s", pwp->pw_name);
+ } else {
+ fprintf(msgfile, " %-8u", USH(asb->sb_uid));
+ }
+ if (grp = getgrgid((int) USH(asb->sb_gid))) {
+ fprintf(msgfile, " %-8s", grp->gr_name);
+ } else {
+ fprintf(msgfile, " %-8u", USH(asb->sb_gid));
+ }
+ switch (asb->sb_mode & S_IFMT) {
+ case S_IFBLK:
+ case S_IFCHR:
+#ifndef _POSIX_SOURCE
+ fprintf(msgfile, "\t%3d, %3d",
+ major(asb->sb_rdev), minor(asb->sb_rdev));
+#endif
+ break;
+ case S_IFREG:
+ fprintf(msgfile, "\t%8ld", asb->sb_size);
+ break;
+ default:
+ fprintf(msgfile, "\t ");
+ }
+ fprintf(msgfile," %3s %2d %02d:%02d ",
+ monnames[atm->tm_mon], atm->tm_mday,
+ atm->tm_hour, atm->tm_min);
+ }
+ fprintf(msgfile, "%s", name);
+ if ((asb->sb_nlink > 1) && (from = islink(name, asb))) {
+ fprintf(msgfile, " == %s", from->l_name);
+ }
+#ifdef S_IFLNK
+ if ((asb->sb_mode & S_IFMT) == S_IFLNK) {
+ fprintf(msgfile, " -> %s", asb->sb_link);
+ }
+#endif /* S_IFLNK */
+ putc('\n', msgfile);
+}
+
+
+/* print_mode - fancy file mode display
+ *
+ * DESCRIPTION
+ *
+ * Print_mode displays a numeric file mode in the standard unix
+ * representation, ala ls (-rwxrwxrwx). No error checking is done
+ * for bad mode combinations. FIFOS, sybmbolic links, sticky bits,
+ * block- and character-special devices are supported if supported
+ * by the hosting implementation.
+ *
+ * PARAMETERS
+ *
+ * ushort mode - The integer representation of the mode to print.
+ */
+
+#ifdef __STDC__
+
+static void print_mode(ushort mode)
+
+#else
+
+static void print_mode(mode)
+ushort mode;
+
+#endif
+{
+ /* Tar does not print the leading identifier... */
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static void print_mode() in list.c\n");
+#endif
+ if (ar_interface != TAR) {
+ switch (mode & S_IFMT) {
+ case S_IFDIR:
+ putc('d', msgfile);
+ break;
+#ifdef S_IFLNK
+ case S_IFLNK:
+ putc('l', msgfile);
+ break;
+#endif /* S_IFLNK */
+ case S_IFBLK:
+ putc('b', msgfile);
+ break;
+ case S_IFCHR:
+ putc('c', msgfile);
+ break;
+#ifdef S_IFIFO
+ case S_IFIFO:
+ putc('p', msgfile);
+ break;
+#endif /* S_IFIFO */
+ case S_IFREG:
+ default:
+ putc('-', msgfile);
+ break;
+ }
+ }
+ putc(mode & 0400 ? 'r' : '-', msgfile);
+ putc(mode & 0200 ? 'w' : '-', msgfile);
+ putc(mode & 0100
+ ? mode & 04000 ? 's' : 'x'
+ : mode & 04000 ? 'S' : '-', msgfile);
+ putc(mode & 0040 ? 'r' : '-', msgfile);
+ putc(mode & 0020 ? 'w' : '-', msgfile);
+ putc(mode & 0010
+ ? mode & 02000 ? 's' : 'x'
+ : mode & 02000 ? 'S' : '-', msgfile);
+ putc(mode & 0004 ? 'r' : '-', msgfile);
+ putc(mode & 0002 ? 'w' : '-', msgfile);
+ putc(mode & 0001
+ ? mode & 01000 ? 't' : 'x'
+ : mode & 01000 ? 'T' : '-', msgfile);
+}
+
+
+/* from_oct - quick and dirty octal conversion
+ *
+ * DESCRIPTION
+ *
+ * From_oct will convert an ASCII representation of an octal number
+ * to the numeric representation. The number of characters to convert
+ * is given by the parameter "digs". If there are less numbers than
+ * specified by "digs", then the routine returns -1.
+ *
+ * PARAMETERS
+ *
+ * int digs - Number to of digits to convert
+ * char *where - Character representation of octal number
+ *
+ * RETURNS
+ *
+ * The value of the octal number represented by the first digs
+ * characters of the string where. Result is -1 if the field
+ * is invalid (all blank, or nonoctal).
+ *
+ * ERRORS
+ *
+ * If the field is all blank, then the value returned is -1.
+ *
+ */
+
+#ifdef __STDC__
+
+static long from_oct(int digs, char *where)
+
+#else
+
+static long from_oct(digs, where)
+int digs; /* number of characters to convert */
+char *where; /* character representation of octal number */
+
+#endif
+{
+ long value;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static long from_oct() in list.c\n");
+#endif
+ while (isspace(*where)) { /* Skip spaces */
+ where++;
+ if (--digs <= 0) {
+ return(-1); /* All blank field */
+ }
+ }
+ value = 0;
+ while (digs > 0 && ISODIGIT(*where)) { /* Scan til nonoctal */
+ value = (value << 3) | (*where++ - '0');
+ --digs;
+ }
+
+ if (digs > 0 && *where && !isspace(*where)) {
+ return(-1); /* Ended on non-space/nul */
+ }
+ return(value);
+}
diff --git a/private/posix/programs/pax/makefile b/private/posix/programs/pax/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/posix/programs/pax/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/posix/programs/pax/mem.c b/private/posix/programs/pax/mem.c
new file mode 100644
index 000000000..c19412194
--- /dev/null
+++ b/private/posix/programs/pax/mem.c
@@ -0,0 +1,144 @@
+/* $Source: /u/mark/src/pax/RCS/mem.c,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * mem.c - memory allocation and manipulation functions
+ *
+ * DESCRIPTION
+ *
+ * These routines are provided for higher level handling of the UNIX
+ * memory allocation functions.
+ *
+ * AUTHOR
+ *
+ * Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
+ *
+ *
+ * Sponsored by The USENIX Association for public distribution.
+ *
+ * Copyright (c) 1989 Mark H. Colburn.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice is 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 Mark H. Colburn and sponsored by The
+ * USENIX Association.
+ *
+ * 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.
+ *
+ * $Log: mem.c,v $
+ * Revision 1.2 89/02/12 10:04:53 mark
+ * 1.2 release fixes
+ *
+ * Revision 1.1 88/12/23 18:02:17 mark
+ * Initial revision
+ *
+ */
+
+#ifndef lint
+static char *ident = "$Id: mem.c,v 1.2 89/02/12 10:04:53 mark Exp $";
+static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n";
+#endif /* ! lint */
+
+
+/* Headers */
+
+#include "pax.h"
+
+
+/* mem_get - allocate memory
+ *
+ * DESCRIPTION
+ *
+ * Mem_get attempts to allocate a block of memory using the malloc
+ * function call. In the event that the memory is not available,
+ * mem_get will display an "Out of memory" message for the user
+ * the first time it encounters the an out of memory situation.
+ * Subsequent calls to mem_get may fail, but no message will be
+ * printed.
+ *
+ * PARAMETERS
+ *
+ * uint len - The amount of memory to allocate
+ *
+ * RETURNS
+ *
+ * Normally returns the pointer to the newly allocated memory. If
+ * an error occurs, NULL is returned, and an error message is
+ * printed.
+ *
+ * ERRORS
+ *
+ * ENOMEM No memory is available
+ */
+
+#ifdef __STDC__
+
+char *mem_get(uint len)
+
+#else
+
+char *mem_get(len)
+uint len; /* amount of memory to get */
+
+#endif
+{
+ char *mem;
+ static short outofmem = 0;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: char *mem_get() in mem.c\n");
+#endif
+ if ((mem = (char *)malloc(len)) == (char *)NULL && !outofmem) {
+ outofmem++;
+ warn("mem_get()", "Out of memory");
+ }
+ return (mem);
+}
+
+
+/* mem_str - duplicate a string into dynamic memory
+ *
+ * DESCRIPTION
+ *
+ * Mem_str attempts to make a copy of string. It allocates space for
+ * the string, and if the allocation was successfull, copies the old
+ * string into the newly allocated space.
+ *
+ * PARAMETERS
+ *
+ * char *str - string to make a copy of
+ *
+ * RETURNS
+ *
+ * Normally returns a pointer to a new string at least as large
+ * as strlen(str) + 1, which contains a copy of the the data
+ * passed in str, plus a null terminator. Returns (char *)NULL
+ * if enough memory to make a copy of str is not available.
+ */
+
+#ifdef __STDC__
+
+char *mem_str(char *str)
+
+#else
+
+char *mem_str(str)
+char *str; /* string to make a copy of */
+
+#endif
+{
+ char *mem;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: char *mem_str() in mem.c\n");
+#endif
+ if (mem = mem_get((uint) strlen(str) + 1)) {
+ strcpy(mem, str);
+ }
+ return (mem);
+}
diff --git a/private/posix/programs/pax/namelist.c b/private/posix/programs/pax/namelist.c
new file mode 100644
index 000000000..605189495
--- /dev/null
+++ b/private/posix/programs/pax/namelist.c
@@ -0,0 +1,553 @@
+/* $Source: /u/mark/src/pax/RCS/namelist.c,v $
+ *
+ * $Revision: 1.6 $
+ *
+ * namelist.c - track filenames given as arguments to tar/cpio/pax
+ *
+ * DESCRIPTION
+ *
+ * Arguments may be regular expressions, therefore all agurments will
+ * be treated as if they were regular expressions, even if they are
+ * not.
+ *
+ * AUTHOR
+ *
+ * Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
+ *
+ * Sponsored by The USENIX Association for public distribution.
+ *
+ * Copyright (c) 1989 Mark H. Colburn.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice is 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 Mark H. Colburn and sponsored by The
+ * USENIX Association.
+ *
+ * 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.
+ *
+ * $Log: namelist.c,v $
+ * Revision 1.6 89/02/13 09:14:48 mark
+ * Fixed problem with directory errors
+ *
+ * Revision 1.5 89/02/12 12:14:00 mark
+ * Fixed misspellings
+ *
+ * Revision 1.4 89/02/12 11:25:19 mark
+ * Modifications to compile and link cleanly under USG
+ *
+ * Revision 1.3 89/02/12 10:40:23 mark
+ * Fixed casting problems
+ *
+ * Revision 1.2 89/02/12 10:04:57 mark
+ * 1.2 release fixes
+ *
+ * Revision 1.1 88/12/23 18:02:17 mark
+ * Initial revision
+ *
+ */
+
+#ifndef lint
+static char *ident = "$Id: namelist.c,v 1.6 89/02/13 09:14:48 mark Exp $";
+static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n";
+#endif /* ! lint */
+
+
+/* Headers */
+
+#include "pax.h"
+
+
+/* Type Definitions */
+
+/*
+ * Structure for keeping track of filenames and lists thereof.
+ */
+struct nm_list {
+ struct nm_list *next;
+ short length; /* cached strlen(name) */
+ char found; /* A matching file has been found */
+ char firstch; /* First char is literally matched */
+ char regexp; /* regexp pattern for item */
+ char name[1]; /* name of file or rexexp */
+};
+
+struct dirinfo {
+ char dirname[PATH_MAX + 1]; /* name of directory */
+ OFFSET where; /* current location in directory */
+ struct dirinfo *next;
+};
+
+
+/* Static Variables */
+
+static struct dirinfo *stack_head = (struct dirinfo *)NULL;
+
+
+/* Function Prototypes */
+
+#ifndef __STDC__
+
+static void pushdir();
+static struct dirinfo *popdir();
+
+#else
+
+static void pushdir(struct dirinfo *info);
+static struct dirinfo *popdir(void);
+
+#endif
+
+
+/* Internal Identifiers */
+
+static struct nm_list *namelast = NULL; /* Points to last name in list */ /* Xn */
+static struct nm_list *namelist = NULL; /* Points to first name in list */ /* Xn */
+
+
+/* addname - add a name to the namelist.
+ *
+ * DESCRIPTION
+ *
+ * Addname adds the name given to the name list. Memory for the
+ * namelist structure is dynamically allocated. If the space for
+ * the structure cannot be allocated, then the program will exit
+ * the an out of memory error message and a non-zero return code
+ * will be returned to the caller.
+ *
+ * PARAMETERS
+ *
+ * char *name - A pointer to the name to add to the list
+ */
+
+#ifdef __STDC__
+
+void add_name(char *name)
+
+#else
+
+void add_name(name)
+char *name; /* pointer to name */
+
+#endif
+{
+ int i; /* Length of string */
+ struct nm_list *p; /* Current struct pointer */
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: void add_name() in namelist.c\n");
+#endif
+ i = strlen(name);
+ p = (struct nm_list *) malloc((unsigned) (i + sizeof(struct nm_list)));
+ if (!p) {
+ fatal("cannot allocate memory for namelist entry\n");
+ }
+ p->next = (struct nm_list *)NULL;
+ p->length = i;
+ strncpy(p->name, name, i);
+ p->name[i] = '\0'; /* Null term */
+ p->found = 0;
+ p->firstch = isalpha(name[0]);
+ if (strchr(name, '*') || strchr(name, '[') || strchr(name, '?')) {
+ p->regexp = 1;
+ }
+ if (namelast) {
+ namelast->next = p;
+ }
+ namelast = p;
+ if (!namelist) {
+ namelist = p;
+ }
+}
+
+
+/* name_match - match a name from an archive with a name from the namelist
+ *
+ * DESCRIPTION
+ *
+ * Name_match attempts to find a name pointed at by p in the namelist.
+ * If no namelist is available, then all filenames passed in are
+ * assumed to match the filename criteria. Name_match knows how to
+ * match names with regular expressions, etc.
+ *
+ * PARAMETERS
+ *
+ * char *p - the name to match
+ *
+ * RETURNS
+ *
+ * Returns 1 if the name is in the namelist, or no name list is
+ * available, otherwise returns 0
+ *
+ */
+
+#ifdef __STDC__
+
+int name_match(char *p)
+
+#else
+
+int name_match(p)
+char *p;
+
+#endif
+{
+ struct nm_list *nlp;
+ int len;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int name_match() in namelist.c\n");
+#endif
+ if ((nlp = namelist) == 0) {/* Empty namelist is easy */
+ return (1);
+ }
+ len = strlen(p);
+ for (; nlp != 0; nlp = nlp->next) {
+ /* If first chars don't match, quick skip */
+ if (nlp->firstch && nlp->name[0] != p[0]) {
+ continue;
+ }
+ /* Regular expressions */
+ if (nlp->regexp) {
+ if (wildmat(nlp->name, p)) {
+ nlp->found = 1; /* Remember it matched */
+ return (1); /* We got a match */
+ }
+ continue;
+ }
+ /* Plain Old Strings */
+ if (nlp->length <= len /* Archive len >= specified */
+ && (p[nlp->length] == '\0' || p[nlp->length] == '/')
+ && strncmp(p, nlp->name, nlp->length) == 0) {
+ /* Name compare */
+ nlp->found = 1; /* Remember it matched */
+ return (1); /* We got a match */
+ }
+ }
+ return (0);
+}
+
+
+/* names_notfound - print names of files in namelist that were not found
+ *
+ * DESCRIPTION
+ *
+ * Names_notfound scans through the namelist for any files which were
+ * named, but for which a matching file was not processed by the
+ * archive. Each of the files is listed on the standard error.
+ *
+ */
+
+#ifdef __STDC__
+
+void names_notfound(void)
+
+#else
+
+void names_notfound()
+
+#endif
+{
+ struct nm_list *nlp;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: void names_notfound() in namelist.c\n");
+#endif
+ for (nlp = namelist; nlp != 0; nlp = nlp->next) {
+ if (!nlp->found) {
+ fprintf(stderr, "%s: %s not found in archive\n",
+ myname, nlp->name);
+ }
+ free(nlp);
+ }
+ namelist = (struct nm_list *)NULL;
+ namelast = (struct nm_list *)NULL;
+}
+
+
+/* name_init - set up to gather file names
+ *
+ * DESCRIPTION
+ *
+ * Name_init sets up the namelist pointers so that we may access the
+ * command line arguments. At least the first item of the command
+ * line (argv[0]) is assumed to be stripped off, prior to the
+ * name_init call.
+ *
+ * PARAMETERS
+ *
+ * int argc - number of items in argc
+ * char **argv - pointer to the command line arguments
+ */
+
+#ifdef __STDC__
+
+void name_init(int argc, char **argv)
+
+#else
+
+void name_init(argc, argv)
+int argc;
+char **argv;
+
+#endif
+{
+ /* Get file names from argv, after options. */
+ n_argc = argc;
+ n_argv = argv;
+}
+
+
+/* name_next - get the next name from argv or the name file.
+ *
+ * DESCRIPTION
+ *
+ * Name next finds the next name which is to be processed in the
+ * archive. If the named file is a directory, then the directory
+ * is recursively traversed for additional file names. Directory
+ * names and locations within the directory are kept track of by
+ * using a directory stack. See the pushdir/popdir function for
+ * more details.
+ *
+ * The names come from argv, after options or from the standard input.
+ *
+ * PARAMETERS
+ *
+ * name - a pointer to a buffer of at least MAX_PATH + 1 bytes long;
+ * statbuf - a pointer to a stat structure
+ *
+ * RETURNS
+ *
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: void name_init() in namelist.c\n");
+#endif
+ * Returns -1 if there are no names left, (e.g. EOF), otherwise returns
+ * 0
+ */
+
+#ifdef __STDC__
+
+int name_next(char *name, Stat *statbuf)
+
+#else
+
+int name_next(name, statbuf)
+char *name;
+Stat *statbuf;
+
+#endif
+{
+ int err = -1; /* Xn */
+ static int in_subdir = 0; /* Xn */
+ static DIR *dirp = NULL; /* Xn */
+ struct dirent *d; /* Xn */
+ static struct dirinfo *curr_dir = NULL; /* Xn */
+ int len; /* Xn */
+
+ do {
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int name_next() in namelist.c\n");
+#endif
+ if (names_from_stdin) {
+ if (lineget(stdin, name) < 0) {
+ return (-1);
+ }
+ if (nameopt(name) < 0) {
+ continue;
+ }
+ } else {
+ if (in_subdir) {
+ if ((d = readdir(dirp)) != (struct dirent *)NULL) {
+ /* Skip . and .. */
+ if (strcmp(d->d_name, ".") == 0 ||
+ strcmp(d->d_name, "..") == 0) {
+ continue;
+ }
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: strcmp() in namelist.c\n");
+#endif
+ if (strlen(d->d_name) +
+ strlen(curr_dir->dirname) >= PATH_MAX) {
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: strlen() in namelist.c\n");
+#endif
+ warn("name too long", d->d_name);
+ continue;
+ }
+ strcpy(name, curr_dir->dirname);
+ strcat(name, d->d_name);
+ } else {
+ closedir(dirp);
+ in_subdir--;
+ curr_dir = popdir();
+ if (in_subdir) {
+ errno = 0;
+ if ((dirp=opendir(curr_dir->dirname)) == (DIR *)NULL) {
+ warn(curr_dir->dirname, "error opening directory (1)");
+ in_subdir--;
+ }
+ seekdir(dirp, curr_dir->where);
+ }
+ continue;
+ }
+ } else if (optind >= n_argc) {
+ return (-1);
+ } else {
+ strcpy(name, n_argv[optind++]);
+ }
+ }
+ if ((err = LSTAT(name, statbuf)) < 0) {
+ warn(name, strerror(errno)); /* Xn */
+ continue;
+ }
+ if (!names_from_stdin && (statbuf->sb_mode & S_IFMT) == S_IFDIR) {
+ if (in_subdir) {
+ curr_dir->where = telldir(dirp);
+ pushdir(curr_dir);
+ closedir(dirp);
+ }
+ in_subdir++;
+
+ /* Build new prototype name */
+ if ((curr_dir = (struct dirinfo *) mem_get(sizeof(struct dirinfo)))
+ == (struct dirinfo *)NULL) {
+ exit(2);
+ }
+ strcpy(curr_dir->dirname, name);
+ len = strlen(curr_dir->dirname);
+ while (len >= 1 && curr_dir->dirname[len - 1] == '/') {
+ len--; /* Delete trailing slashes */
+ }
+ curr_dir->dirname[len++] = '/'; /* Now add exactly one back */
+ curr_dir->dirname[len] = '\0';/* Make sure null-terminated */
+ curr_dir->where = 0;
+
+ errno = 0;
+ do {
+ if ((dirp = opendir(curr_dir->dirname)) == (DIR *)NULL) {
+ warn(curr_dir->dirname, "error opening directory (2)");
+ if (in_subdir > 1) {
+ curr_dir = popdir();
+ }
+ in_subdir--;
+ err = -1;
+ continue;
+ } else {
+ seekdir(dirp, curr_dir->where);
+ }
+ } while (in_subdir && (! dirp));
+ }
+ } while (err < 0);
+ return (0);
+}
+
+
+/* name_gather - gather names in a list for scanning.
+ *
+ * DESCRIPTION
+ *
+ * Name_gather takes names from the command line and adds them to
+ * the name list.
+ *
+ * FIXME
+ *
+ * We could hash the names if we really care about speed here.
+ */
+
+#ifdef __STDC__
+
+void name_gather(void)
+
+#else
+
+void name_gather()
+
+#endif
+{
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: void name_gather() in namelist.c\n");
+#endif
+ while (optind < n_argc) {
+ add_name(n_argv[optind++]);
+ }
+}
+
+
+/* pushdir - pushes a directory name on the directory stack
+ *
+ * DESCRIPTION
+ *
+ * The pushdir function puses the directory structure which is pointed
+ * to by "info" onto a stack for later processing. The information
+ * may be retrieved later with a call to popdir().
+ *
+ * PARAMETERS
+ *
+ * dirinfo *info - pointer to directory structure to save
+ */
+
+#ifdef __STDC__
+
+static void pushdir(struct dirinfo *info)
+
+#else
+
+static void pushdir(info)
+struct dirinfo *info;
+
+#endif
+{
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static void pushdir() in namelist.c\n");
+#endif
+ if (stack_head == (struct dirinfo *)NULL) {
+ stack_head = info;
+ stack_head->next = (struct dirinfo *)NULL;
+ } else {
+ info->next = stack_head;
+ stack_head = info;
+ }
+}
+
+
+/* popdir - pop a directory structure off the directory stack.
+ *
+ * DESCRIPTION
+ *
+ * The popdir function pops the most recently pushed directory
+ * structure off of the directory stack and returns it to the calling
+ * function.
+ *
+ * RETURNS
+ *
+ * Returns a pointer to the most recently pushed directory structure
+ * or NULL if the stack is empty.
+ */
+
+#ifdef __STDC__
+
+static struct dirinfo *popdir(void)
+
+#else
+
+static struct dirinfo *popdir()
+
+#endif
+{
+ struct dirinfo *tmp;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static struct dirinfo *popdir() in namelist.c\n");
+#endif
+ if (stack_head == (struct dirinfo *)NULL) {
+ return((struct dirinfo *)NULL);
+ } else {
+ tmp = stack_head;
+ stack_head = stack_head->next;
+ }
+ return(tmp);
+}
diff --git a/private/posix/programs/pax/names.c b/private/posix/programs/pax/names.c
new file mode 100644
index 000000000..00a49302a
--- /dev/null
+++ b/private/posix/programs/pax/names.c
@@ -0,0 +1,268 @@
+/* $Source: /u/mark/src/pax/RCS/names.c,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * names.c - Look up user and/or group names.
+ *
+ * DESCRIPTION
+ *
+ * These functions support UID and GID name lookup. The results are
+ * cached to improve performance.
+ *
+ * AUTHOR
+ *
+ * Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
+ *
+ * Sponsored by The USENIX Association for public distribution.
+ *
+ * Copyright (c) 1989 Mark H. Colburn.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice is 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 Mark H. Colburn and sponsored by The
+ * USENIX Association.
+ *
+ * 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.
+ *
+ * $Log: names.c,v $
+ * Revision 1.2 89/02/12 10:05:05 mark
+ * 1.2 release fixes
+ *
+ * Revision 1.1 88/12/23 18:02:19 mark
+ * Initial revision
+ *
+ */
+
+#ifndef lint
+static char *ident = "$Id: names.c,v 1.2 89/02/12 10:05:05 mark Exp $";
+static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n";
+#endif /* ! lint */
+
+
+/* Headers */
+
+#include "pax.h"
+
+
+/* Defines */
+
+#define myuid ( my_uid < 0? (my_uid = getuid()): my_uid )
+#define mygid ( my_gid < 0? (my_gid = getgid()): my_gid )
+
+
+/* Internal Identifiers */
+
+static int saveuid = -993;
+static char saveuname[TUNMLEN];
+static int my_uid = -993;
+
+static int savegid = -993;
+static char savegname[TGNMLEN];
+static int my_gid = -993;
+
+
+/* finduname - find a user or group name from a uid or gid
+ *
+ * DESCRIPTION
+ *
+ * Look up a user name from a uid/gid, maintaining a cache.
+ *
+ * PARAMETERS
+ *
+ * char uname[] - name (to be returned to user)
+ * int uuid - id of name to find
+ *
+ *
+ * RETURNS
+ *
+ * Returns a name which is associated with the user id given. If there
+ * is not name which corresponds to the user-id given, then a pointer
+ * to a string of zero length is returned.
+ *
+ * FIXME
+ *
+ * 1. for now it's a one-entry cache.
+ * 2. The "-993" is to reduce the chance of a hit on the first lookup.
+ */
+
+#ifdef __STDC__
+
+char *finduname(int uuid)
+
+#else
+
+char *finduname(uuid)
+int uuid;
+
+#endif
+{
+ struct passwd *pw;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: char *finduname() in names.c\n");
+#endif
+ if (uuid != saveuid) {
+ saveuid = uuid;
+ saveuname[0] = '\0';
+ pw = getpwuid(uuid);
+ if (pw) {
+ strncpy(saveuname, pw->pw_name, TUNMLEN);
+ }
+ }
+ return(saveuname);
+}
+
+
+/* finduid - get the uid for a given user name
+ *
+ * DESCRIPTION
+ *
+ * This does just the opposit of finduname. Given a user name it
+ * finds the corresponding UID for that name.
+ *
+ * PARAMETERS
+ *
+ * char uname[] - username to find a UID for
+ *
+ * RETURNS
+ *
+ * The UID which corresponds to the uname given, if any. If no UID
+ * could be found, then the UID which corrsponds the user running the
+ * program is returned.
+ *
+ */
+
+#ifdef __STDC__
+
+int finduid(char *uname)
+
+#else
+
+int finduid(uname)
+char *uname;
+
+#endif
+{
+ struct passwd *pw;
+ extern struct passwd *getpwnam();
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int finduid() in names.c\n");
+#endif
+ //printf ("myuid: %d\n", myuid);
+ return (myuid);
+ if (uname[0] != saveuname[0]/* Quick test w/o proc call */
+ ||0 != strncmp(uname, saveuname, TUNMLEN)) {
+ strncpy(saveuname, uname, TUNMLEN);
+ pw = getpwnam(uname);
+ if (pw) {
+ saveuid = pw->pw_uid;
+ } else {
+ saveuid = myuid;
+ }
+ }
+ return (saveuid);
+}
+
+
+/* findgname - look up a group name from a gid
+ *
+ * DESCRIPTION
+ *
+ * Look up a group name from a gid, maintaining a cache.
+ *
+ *
+ * PARAMETERS
+ *
+ * int ggid - goupid of group to find
+ *
+ * RETURNS
+ *
+ * A string which is associated with the group ID given. If no name
+ * can be found, a string of zero length is returned.
+ */
+
+#ifdef __STDC__
+
+char *findgname(int ggid)
+
+#else
+
+char *findgname(ggid)
+int ggid;
+
+#endif
+{
+ struct group *gr;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: char *findgname() in names.c\n");
+#endif
+ if (ggid != savegid) {
+ savegid = ggid;
+ savegname[0] = '\0';
+#ifndef _POSIX_SOURCE /* Xn */
+ setgrent();
+#endif /* Xn */
+ gr = getgrgid(ggid);
+ if (gr) {
+ strncpy(savegname, gr->gr_name, TGNMLEN);
+ }
+ }
+ return(savegname);
+}
+
+
+
+/* findgid - get the gid for a given group name
+ *
+ * DESCRIPTION
+ *
+ * This does just the opposit of finduname. Given a group name it
+ * finds the corresponding GID for that name.
+ *
+ * PARAMETERS
+ *
+ * char uname[] - groupname to find a GID for
+ *
+ * RETURNS
+ *
+ * The GID which corresponds to the uname given, if any. If no GID
+ * could be found, then the GID which corrsponds the group running the
+ * program is returned.
+ *
+ */
+
+#ifdef __STDC__
+
+int findgid(char *gname)
+
+#else
+
+int findgid(gname)
+char *gname;
+
+#endif
+{
+ struct group *gr;
+
+ /* Quick test w/o proc call */
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int findgid() in names.c\n");
+#endif
+ if (gname[0] != savegname[0] || strncmp(gname, savegname, TUNMLEN) != 0) {
+ strncpy(savegname, gname, TUNMLEN);
+ gr = getgrnam(gname);
+ if (gr) {
+ savegid = gr->gr_gid;
+ } else {
+ savegid = mygid;
+ }
+ }
+ return (savegid);
+}
diff --git a/private/posix/programs/pax/pass.c b/private/posix/programs/pax/pass.c
new file mode 100644
index 000000000..a563bb6c2
--- /dev/null
+++ b/private/posix/programs/pax/pass.c
@@ -0,0 +1,183 @@
+/* $Source: /u/mark/src/pax/RCS/pass.c,v $
+ *
+ * $Revision: 1.3 $
+ *
+ * pass.c - handle the pass option of cpio
+ *
+ * DESCRIPTION
+ *
+ * These functions implement the pass options in PAX. The pass option
+ * copies files from one directory hierarchy to another.
+ *
+ * AUTHOR
+ *
+ * Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
+ *
+ * Sponsored by The USENIX Association for public distribution.
+ *
+ * Copyright (c) 1989 Mark H. Colburn.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice is 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 Mark H. Colburn and sponsored by The
+ * USENIX Association.
+ *
+ * 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.
+ *
+ * $Log: pass.c,v $
+ * Revision 1.3 89/02/12 10:29:51 mark
+ * Fixed misspelling of Replstr
+ *
+ * Revision 1.2 89/02/12 10:05:09 mark
+ * 1.2 release fixes
+ *
+ * Revision 1.1 88/12/23 18:02:20 mark
+ * Initial revision
+ *
+ */
+
+#ifndef lint
+static char *ident = "$Id: pass.c,v 1.3 89/02/12 10:29:51 mark Exp $";
+static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n";
+#endif /* ! lint */
+
+
+/* Headers */
+
+#include "pax.h"
+
+
+/* pass - copy within the filesystem
+ *
+ * DESCRIPTION
+ *
+ * Pass copies the named files from the current directory hierarchy to
+ * the directory pointed to by dirname.
+ *
+ * PARAMETERS
+ *
+ * char *dirname - name of directory to copy named files to.
+ *
+ */
+
+#ifdef __STDC__
+
+void pass(char *dirname) /* Xn */
+
+#else
+
+void pass(dirname) /* Xn */
+char *dirname;
+
+#endif
+{
+ char name[PATH_MAX + 1];
+ int fd;
+ Stat sb;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: void pass() in pass.c\n");
+#endif
+ while (name_next(name, &sb) >= 0 && (fd = openin(name, &sb)) >= 0) {
+
+ if (rplhead != (Replstr *)NULL) {
+ rpl_name(name);
+ }
+ if (get_disposition("pass", name) || get_newname(name, sizeof(name))) {
+ /* skip file... */
+ if (fd) {
+ close(fd);
+ }
+ continue;
+ }
+
+ if (passitem(name, &sb, fd, dirname)) {
+ close(fd);
+ }
+ if (f_verbose) {
+ fprintf(stderr, "%s/%s\n", dirname, name);
+ }
+ }
+}
+
+
+/* passitem - copy one file
+ *
+ * DESCRIPTION
+ *
+ * Passitem copies a specific file to the named directory
+ *
+ * PARAMETERS
+ *
+ * char *from - the name of the file to open
+ * Stat *asb - the stat block associated with the file to copy
+ * int ifd - the input file descriptor for the file to copy
+ * char *dir - the directory to copy it to
+ *
+ * RETURNS
+ *
+ * Returns given input file descriptor or -1 if an error occurs.
+ *
+ * ERRORS
+ */
+
+#ifdef __STDC__
+
+int passitem(char *from, Stat *asb, int ifd, char *dir)
+
+#else
+
+int passitem(from, asb, ifd, dir)
+char *from;
+Stat *asb;
+int ifd;
+char *dir;
+
+#endif
+{
+ int ofd;
+#ifdef _POSIX_SOURCE /* Xn */
+ struct utimbuf tstamp; /* Xn */
+#else /* Xn */
+ time_t tstamp[2];
+#endif /* Xn */
+ char to[PATH_MAX + 1];
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int passitem() in pass.c\n");
+#endif
+ if (nameopt(strcat(strcat(strcpy(to, dir), "/"), from)) < 0) {
+ return (-1);
+ }
+#if 0 /* NIST-PCTS */
+ if (asb->sb_nlink > 1) {
+#else /* NIST-PCTS */
+ if (asb->sb_nlink > 1 && (asb->sb_mode & S_IFMT) != S_IFDIR) { /* NIST-PCTS */
+#endif /* NIST-PCTS */
+ linkto(to, asb);
+ }
+ if (f_link && islink(from, asb) == (Link *)NULL) {
+ linkto(from, asb);
+ }
+ if ((ofd = openout(to, asb, islink(to, asb), 1)) < 0) {
+ return (-1);
+ }
+ if (ofd > 0) {
+ passdata(from, ifd, to, ofd);
+ }
+#ifdef _POSIX_SOURCE /* Xn */
+ tstamp.actime = asb->sb_atime; /* Xn */
+ tstamp.modtime = f_mtime ? asb->sb_mtime : time((time_t *) 0); /* Xn */
+ (void) utime(to, &tstamp); /* Xn */
+#else /* Xn */
+ tstamp[0] = asb->sb_atime;
+ tstamp[1] = f_mtime ? asb->sb_mtime : time((time_t *) 0);
+ (void) utime(to, tstamp); /* Xn */
+#endif /* Xn */
+ return (ifd);
+}
diff --git a/private/posix/programs/pax/pathname.c b/private/posix/programs/pax/pathname.c
new file mode 100644
index 000000000..368729599
--- /dev/null
+++ b/private/posix/programs/pax/pathname.c
@@ -0,0 +1,252 @@
+/* $Source: /u/mark/src/pax/RCS/pathname.c,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * pathname.c - directory/pathname support functions
+ *
+ * DESCRIPTION
+ *
+ * These functions provide directory/pathname support for PAX
+ *
+ * AUTHOR
+ *
+ * Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
+ *
+ * Sponsored by The USENIX Association for public distribution.
+ *
+ * Copyright (c) 1989 Mark H. Colburn.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice is 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 Mark H. Colburn and sponsored by The
+ * USENIX Association.
+ *
+ * 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.
+ *
+ * $Log: pathname.c,v $
+ * Revision 1.2 89/02/12 10:05:13 mark
+ * 1.2 release fixes
+ *
+ * Revision 1.1 88/12/23 18:02:21 mark
+ * Initial revision
+ *
+ */
+
+#ifndef lint
+static char *ident = "$Id: pathname.c,v 1.2 89/02/12 10:05:13 mark Exp $";
+static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n";
+#endif /* ! lint */
+
+
+/* Headers */
+
+#include "pax.h"
+
+
+/* dirneed - checks for the existance of directories and possibly create
+ *
+ * DESCRIPTION
+ *
+ * Dirneed checks to see if a directory of the name pointed to by name
+ * exists. If the directory does exist, then dirneed returns 0. If
+ * the directory does not exist and the f_dir_create flag is set,
+ * then dirneed will create the needed directory, recursively creating
+ * any needed intermediate directory.
+ *
+ * If f_dir_create is not set, then no directories will be created
+ * and a value of -1 will be returned if the directory does not
+ * exist.
+ *
+ * PARAMETERS
+ *
+ * name - name of the directory to create
+ *
+ * RETURNS
+ *
+ * Returns a 0 if the creation of the directory succeeded or if the
+ * directory already existed. If the f_dir_create flag was not set
+ * and the named directory does not exist, or the directory creation
+ * failed, a -1 will be returned to the calling routine.
+ */
+
+#ifdef __STDC__
+
+int dirneed(char *name)
+
+#else
+
+int dirneed(name)
+char *name;
+
+#endif
+{
+ char *cp;
+ char *last;
+ int ok;
+ static Stat sb;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int dirneed() in pathname.c\n");
+#endif
+ last = (char *)NULL;
+ for (cp = name; *cp;) {
+ if (*cp++ == '/') {
+ last = cp;
+ }
+ }
+ if (last == (char *)NULL) {
+ return (STAT(".", &sb));
+ }
+ *--last = '\0';
+ ok = STAT(*name ? name : ".", &sb) == 0
+ ? ((sb.sb_mode & S_IFMT) == S_IFDIR)
+ : (f_dir_create && dirneed(name) == 0 && dirmake(name, &sb) == 0);
+ *last = '/';
+ return (ok ? 0 : -1);
+}
+
+
+/* nameopt - optimize a pathname
+ *
+ * DESCRIPTION
+ *
+ * Confused by "<symlink>/.." twistiness. Returns the number of final
+ * pathname elements (zero for "/" or ".") or -1 if unsuccessful.
+ *
+ * PARAMETERS
+ *
+ * char *begin - name of the path to optimize
+ *
+ * RETURNS
+ *
+ * Returns 0 if successful, non-zero otherwise.
+ *
+ */
+
+#ifdef __STDC__
+
+int nameopt(char *begin)
+
+#else
+
+int nameopt(begin)
+char *begin;
+
+#endif
+{
+ char *name;
+ char *item;
+ int idx;
+ int absolute;
+ char *element[PATHELEM];
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: ok = STAT() in pathname.c\n");
+#endif
+ absolute = (*(name = begin) == '/');
+ idx = 0;
+ for (;;) {
+ if (idx == PATHELEM) {
+ warn(begin, "Too many elements");
+ return (-1);
+ }
+ while (*name == '/') {
+ ++name;
+ }
+ if (*name == '\0') {
+ break;
+ }
+ element[idx] = item = name;
+ while (*name && *name != '/') {
+ ++name;
+ }
+ if (*name) {
+ *name++ = '\0';
+ }
+ if (strcmp(item, "..") == 0) {
+ if (idx == 0) {
+ if (!absolute) {
+ ++idx;
+ }
+ } else if (strcmp(element[idx - 1], "..") == 0) {
+ ++idx;
+ } else {
+ --idx;
+ }
+ } else if (strcmp(item, ".") != 0) {
+ ++idx;
+ }
+ }
+ if (idx == 0) {
+ element[idx++] = absolute ? "" : ".";
+ }
+ element[idx] = (char *)NULL;
+ name = begin;
+ if (absolute) {
+ *name++ = '/';
+ }
+ for (idx = 0; item = element[idx]; ++idx, *name++ = '/') {
+ while (*item) {
+ *name++ = *item++;
+ }
+ }
+ *--name = '\0';
+ return (idx);
+}
+
+
+/* dirmake - make a directory
+ *
+ * DESCRIPTION
+ *
+ * Dirmake makes a directory with the appropritate permissions.
+ *
+ * PARAMETERS
+ *
+ * char *name - Name of directory make
+ * Stat *asb - Stat structure of directory to make
+ *
+ * RETURNS
+ *
+ * Returns zero if successful, -1 otherwise.
+ *
+ */
+
+#ifdef __STDC__
+
+int dirmake(char *name, Stat *asb)
+
+#else
+
+int dirmake(name, asb)
+char *name;
+Stat *asb;
+
+#endif
+{
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int dirmake() in pathname.c\n");
+#endif
+
+#ifdef DF_POSIX //DF_DSC permission stuff no working in POSIX
+// mkdir (name,0777);
+// return (0);
+#endif
+
+ if (mkdir(name, (int) (asb->sb_mode & S_IPOPN)) < 0) {
+ return (-1);
+ }
+ if (asb->sb_mode & S_IPEXE) {
+//printf("name X%sX mode X%ldX\n", name, asb->sb_mode);
+ chmod(name, (int) (asb->sb_mode & S_IPERM));
+ }
+ if (f_owner) {
+ chown(name, (int) asb->sb_uid, (int) asb->sb_gid);
+ }
+ return (0);
+}
diff --git a/private/posix/programs/pax/pax.c b/private/posix/programs/pax/pax.c
new file mode 100644
index 000000000..a879ed720
--- /dev/null
+++ b/private/posix/programs/pax/pax.c
@@ -0,0 +1,557 @@
+/* $Source: /u/mark/src/pax/RCS/pax.c,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * DESCRIPTION
+ *
+ * Pax is the archiver described in IEEE P1003.2. It is an archiver
+ * which understands both tar and cpio archives and has a new interface.
+ *
+ * SYNOPSIS
+ *
+ * pax -[cimopuvy] [-f archive] [-s replstr] [-t device] [pattern...]
+ * pax -r [-cimopuvy] [-f archive] [-s replstr] [-t device] [pattern...]
+ * pax -w [-adimuvy] [-b blocking] [-f archive] [-s replstr]...]
+ * [-t device][-x format][pathname...]
+ * pax -r -w [-ilmopuvy][-s replstr][pathname...] directory
+ *
+ * DESCRIPTION
+ *
+ * PAX - POSIX conforming tar and cpio archive handler. This
+ * program implements POSIX conformant versions of tar, cpio and pax
+ * archive handlers for UNIX. These handlers have defined befined
+ * by the IEEE P1003.2 commitee.
+ *
+ * COMPILATION
+ *
+ * A number of different compile time configuration options are
+ * available, please see the Makefile and config.h for more details.
+ *
+ * AUTHOR
+ *
+ * Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
+ *
+ *
+ * Sponsored by The USENIX Association for public distribution.
+ *
+ * Copyright (c) 1989 Mark H. Colburn.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice is 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 Mark H. Colburn and sponsored by The
+ * USENIX Association.
+ *
+ * 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.
+ *
+ * $Log: pax.c,v $
+ * Revision 1.2 89/02/12 10:05:17 mark
+ * 1.2 release fixes
+ *
+ * Revision 1.1 88/12/23 18:02:23 mark
+ * Initial revision
+ *
+ */
+
+#ifndef lint
+static char *ident = "$Id: pax.c,v 1.2 89/02/12 10:05:17 mark Exp $";
+static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n";
+#endif /* ! lint */
+
+
+/* Headers */
+
+#define NO_EXTERN
+#include "pax.h"
+
+
+/* Globally Available Identifiers */
+
+char *ar_file; /* File containing name of archive */
+char *bufend; /* End of data within archive buffer */
+char *bufstart; /* Archive buffer */
+char *bufidx; /* Archive buffer index */
+char *myname; /* name of executable (argv[0]) */
+char **n_argv; /* Argv used by name routines */
+int n_argc; /* Argc used by name routines */
+int archivefd; /* Archive file descriptor */
+int blocking; /* Size of each block, in records */
+int gid; /* Group ID */
+int head_standard; /* true if archive is POSIX format */
+int ar_interface; /* defines interface we are using */
+int ar_format; /* defines current archve format */
+int mask; /* File creation mask */
+int ttyf; /* For interactive queries */
+int uid; /* User ID */
+int names_from_stdin; /* names for files are from stdin */
+OFFSET total; /* Total number of bytes transferred */
+short f_access_time; /* Reset access times of input files */
+short areof; /* End of input volume reached */
+short f_dir_create; /* Create missing directories */
+short f_append; /* Add named files to end of archive */
+short f_create; /* create a new archive */
+short f_extract; /* Extract named files from archive */
+short f_follow_links; /* follow symbolic links */
+short f_interactive; /* Interactivly extract files */
+short f_linksleft; /* Report on unresolved links */
+short f_list; /* List files on the archive */
+short f_modified; /* Don't restore modification times */
+short f_verbose; /* Turn on verbose mode */
+short f_link; /* link files where possible */
+short f_owner; /* extract files as the user */
+short f_pass; /* pass files between directories */
+short f_newer; /* append files to archive if newer */
+short f_disposition; /* ask for file disposition */
+short f_reverse_match; /* Reverse sense of pattern match */
+short f_mtime; /* Retain file modification time */
+short f_unconditional; /* Copy unconditionally */
+short f_quiet; /* Don't beep for volume change */ /* Xn */
+time_t now = 0; /* Current time */
+uint arvolume; /* Volume number */
+uint blocksize = BLOCKSIZE; /* Archive block size */
+FILE *msgfile; /* message outpu file stdout/stderr */
+Replstr *rplhead = (Replstr *)NULL; /* head of replstr list */
+Replstr *rpltail; /* pointer to tail of replstr list */
+
+
+/* Function Prototypes */
+
+#ifdef __STDC__
+
+static void usage(void);
+static OFFSET pax_optsize(char *);
+
+#else /* !__STDC__ */
+
+static void usage();
+static OFFSET pax_optsize();
+
+#endif /* __STDC__ */
+
+
+/* main - main routine for handling all archive formats.
+ *
+ * DESCRIPTION
+ *
+ * Set up globals and call the proper interface as specified by the user.
+ *
+ * PARAMETERS
+ *
+ * int argc - count of user supplied arguments
+ * char **argv - user supplied arguments
+ *
+ * RETURNS
+ *
+ * Returns an exit code of 0 to the parent process.
+ */
+
+#ifdef __STDC__
+
+int main(int argc, char **argv)
+
+#else
+
+int main(argc, argv)
+int argc;
+char **argv;
+
+#endif
+{
+ /* strip the pathname off of the name of the executable */
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int main() in pax.c\n");
+#endif
+ if ((myname = strrchr(argv[0], '/')) != (char *)NULL) {
+ myname++;
+ } else {
+ myname = argv[0];
+ }
+
+ /* set upt for collecting other command line arguments */
+ name_init(argc, argv);
+
+ /* get all our necessary information */
+ mask = umask(0);
+ uid = getuid();
+ gid = getgid();
+ now = time((time_t *) 0);
+
+ /* open terminal for interactive queries */
+ ttyf = open_tty();
+
+ if (strcmp(myname, "tar")==0) {
+ do_tar(argc, argv);
+ } else if (strcmp(myname, "cpio")==0) {
+ do_cpio(argc, argv);
+ } else {
+ do_pax(argc, argv);
+ }
+ return 0; /* Xn */
+ /* NOTREACHED */
+}
+
+
+/* do_pax - provide a PAX conformant user interface for archive handling
+ *
+ * DESCRIPTION
+ *
+ * Process the command line parameters given, doing some minimal sanity
+ * checking, and then launch the specified archiving functions.
+ *
+ * PARAMETERS
+ *
+ * int ac - A count of arguments in av. Should be passed argc
+ * from main
+ * char **av - A pointer to an argument list. Should be passed
+ * argv from main
+ *
+ * RETURNS
+ *
+ * Normally returns 0. If an error occurs, -1 is returned
+ * and state is set to reflect the error.
+ *
+ */
+
+#ifdef __STDC__
+
+int do_pax(int ac, char **av)
+
+#else
+
+int do_pax(ac, av)
+int ac; /* argument counter */
+char **av; /* arguments */
+
+#endif
+{
+ int c;
+ char *dirname;
+ Stat st;
+
+ /* default input/output file for PAX is STDIN/STDOUT */
+ ar_file = "-";
+
+ /*
+ * set up the flags to reflect the default pax inteface. Unfortunately
+ * the pax interface has several options which are completely opposite
+ * of the tar and/or cpio interfaces...
+ */
+ f_unconditional = 1;
+ f_mtime = 1;
+ f_dir_create = 1;
+ f_list = 1;
+ blocksize = 0;
+ blocking = 0;
+ ar_interface = PAX;
+ ar_format = TAR; /* default interface if none given for -w */
+ msgfile=stdout;
+
+#ifdef _POSIX2_SOURCE /* Xn */
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int do_pax() in pax.c\n");
+#endif
+ while ((c = getopt(ac, (const char * const *) av, "ab:cdf:ilmopqrs:t:uvwx:y")) != EOF) { /* Xn */
+#else /* Xn */
+ while ((c = getopt(ac, av, "ab:cdf:ilmopqrs:t:uvwx:y")) != EOF) { /* Xn */
+#endif /* Xn */
+ switch (c) {
+ case 'a':
+ f_append = 1;
+ f_list = 0;
+ break;
+ case 'b':
+ if ((blocksize = pax_optsize(optarg)) == 0) {
+ fatal("Bad block size");
+ }
+ break;
+ case 'c':
+ f_reverse_match = 1;
+ break;
+ case 'd':
+ f_dir_create = 0;
+ break;
+ case 'f':
+ if (blocksize == 0) {
+ blocking = 1;
+ blocksize = 1 * BLOCKSIZE;
+ }
+ ar_file = optarg;
+ break;
+ case 'i':
+ f_interactive = 1;
+ break;
+ case 'l':
+ f_link = 1;
+ break;
+ case 'm':
+ f_mtime = 0;
+ break;
+ case 'o':
+ f_owner = 1;
+ break;
+ case 'p':
+ f_access_time = 1;
+ break;
+ case 'q': /* Xn */
+ f_quiet = 1; /* Xn */
+ break; /* Xn */
+ case 'r':
+ if (f_create) {
+ f_create = 0;
+ f_pass = 1;
+ } else {
+ f_list = 0;
+ f_extract = 1;
+ }
+ msgfile=stderr;
+ break;
+ case 's':
+ add_replstr(optarg);
+ break;
+ case 't':
+ if (blocksize == 0) {
+ blocking = 1;
+ blocksize = 10 * BLOCKSIZE;
+ }
+ ar_file = optarg;
+ break;
+ case 'u':
+ f_unconditional = 1;
+ break;
+ case 'v':
+ f_verbose = 1;
+ break;
+ case 'w':
+ if (f_extract) {
+ f_extract = 0;
+ f_pass = 1;
+ } else {
+ f_list = 0;
+ f_create = 1;
+ }
+ msgfile=stderr;
+ break;
+ case 'x':
+ if (strcmp(optarg, "ustar") == 0) {
+ ar_format = TAR;
+ } else if (strcmp(optarg, "cpio") == 0) {
+ ar_format = CPIO;
+ } else {
+ usage();
+ }
+ break;
+ case 'y':
+ f_disposition = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (blocksize == 0) {
+ blocking = 1;
+ blocksize = blocking * BLOCKSIZE;
+ }
+ buf_allocate((OFFSET) blocksize);
+
+ if (f_extract || f_list) {
+ open_archive(AR_READ);
+ get_archive_type();
+ read_archive();
+//DF_MSS
+exit(1);
+ } else if (f_append) { /* this block used to be after the f_create--Xn */
+ open_archive(AR_APPEND);
+ get_archive_type();
+ append_archive();
+ } else if (f_create) {
+ if (optind >= n_argc) {
+ names_from_stdin++; /* args from stdin */
+ }
+ open_archive(AR_WRITE);
+ create_archive();
+ } else if (f_pass && optind < n_argc) {
+ dirname = n_argv[--n_argc];
+ if (LSTAT(dirname, &st) < 0) {
+ fatal(strerror(errno)); /* Xn */
+ }
+ if ((st.sb_mode & S_IFMT) != S_IFDIR) {
+ fatal("Not a directory");
+ }
+ if (optind >= n_argc) {
+ names_from_stdin++; /* args from stdin */
+ }
+ pass(dirname);
+ } else {
+ usage();
+ }
+
+ return (0);
+}
+
+
+/* get_archive_type - determine input archive type from archive header
+ *
+ * DESCRIPTION
+ *
+ * reads the first block of the archive and determines the archive
+ * type from the data. If the archive type cannot be determined,
+ * processing stops, and a 1 is returned to the caller. If verbose
+ * mode is on, then the archive type will be printed on the standard
+ * error device as it is determined.
+ *
+ * FIXME
+ *
+ * be able to understand TAR and CPIO magic numbers
+ */
+
+#ifdef __STDC__
+
+void get_archive_type(void)
+
+#else
+
+void get_archive_type()
+
+#endif
+{
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: void get_archive_type() in pax.c\n");
+#endif
+ if (ar_read() != 0) {
+ fatal("Unable to determine archive type.");
+ }
+ if (strncmp(bufstart, "070707", 6) == 0) {
+ ar_format = CPIO;
+ if (f_verbose) {
+ fputs("CPIO format archive\n", stderr);
+ }
+ } else if (strncmp(&bufstart[257], "ustar", 5) == 0) {
+ ar_format = TAR;
+ if (f_verbose) {
+ fputs("USTAR format archive\n", stderr);
+ }
+ } else {
+ ar_format = TAR;
+ }
+}
+
+
+/* pax_optsize - interpret a size argument
+ *
+ * DESCRIPTION
+ *
+ * Recognizes suffixes for blocks (512-bytes), k-bytes and megabytes.
+ * Also handles simple expressions containing '+' for addition.
+ *
+ * PARAMETERS
+ *
+ * char *str - A pointer to the string to interpret
+ *
+ * RETURNS
+ *
+ * Normally returns the value represented by the expression in the
+ * the string.
+ *
+ * ERRORS
+ *
+ * If the string cannot be interpretted, the program will fail, since
+ * the buffering will be incorrect.
+ *
+ */
+
+#ifdef __STDC__
+
+static OFFSET pax_optsize(char *str)
+
+#else
+
+static OFFSET pax_optsize(str)
+char *str; /* pointer to string to interpret */
+
+#endif
+{
+ char *idx;
+ OFFSET number; /* temporary storage for current number */
+ OFFSET result; /* cumulative total to be returned to caller */
+
+ result = 0;
+ idx = str;
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static OFFSET pax_optsize() in pax.c\n");
+#endif
+ for (;;) {
+ number = 0;
+ while (*idx >= '0' && *idx <= '9')
+ number = number * 10 + *idx++ - '0';
+ switch (*idx++) {
+ case 'b':
+ result += number * 512L;
+ continue;
+ case 'k':
+ result += number * 1024L;
+ continue;
+ case 'm':
+ result += number * 1024L * 1024L;
+ continue;
+ case '+':
+ result += number;
+ continue;
+ case '\0':
+ result += number;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ if (*--idx) {
+ fatal("Unrecognizable value");
+ }
+ return (result);
+}
+
+
+/* usage - print a helpful message and exit
+ *
+ * DESCRIPTION
+ *
+ * Usage prints out the usage message for the PAX interface and then
+ * exits with a non-zero termination status. This is used when a user
+ * has provided non-existant or incompatible command line arguments.
+ *
+ * RETURNS
+ *
+ * Returns an exit status of 1 to the parent process.
+ *
+ */
+
+#ifdef __STDC__
+
+static void usage(void)
+
+#else
+
+static void usage()
+
+#endif
+{
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static void usage() in pax.c\n");
+#endif
+ fprintf(stderr, "Usage: %s -[cimopuvy] [-f archive] [-s replstr] [-t device] [pattern...]\n",
+ myname);
+ fprintf(stderr, " %s -r [-cimopuvy] [-f archive] [-s replstr] [-t device] [pattern...]\n",
+ myname);
+ fprintf(stderr, " %s -w [-adimuvy] [-b blocking] [-f archive] [-s replstr]\n [-t device] [-x format] [pathname...]\n",
+ myname);
+ fprintf(stderr, " %s -r -w [-ilmopuvy] [-s replstr] [pathname...] directory\n",
+ myname);
+ fprintf(stderr, "\nFor more information on %s syntax, see Command Reference\n", myname);
+ fprintf(stderr, "Help in the Windows NT Help file.");
+ exit(1);
+}
diff --git a/private/posix/programs/pax/pax.h b/private/posix/programs/pax/pax.h
new file mode 100644
index 000000000..2854af4f2
--- /dev/null
+++ b/private/posix/programs/pax/pax.h
@@ -0,0 +1,408 @@
+/* $Source: /u/mark/src/pax/RCS/pax.h,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * pax.h - defnitions for entire program
+ *
+ * DESCRIPTION
+ *
+ * This file contains most all of the definitions required by the PAX
+ * software. This header is included in every source file.
+ *
+ * AUTHOR
+ *
+ * Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
+ *
+ * Sponsored by The USENIX Association for public distribution.
+ *
+ * Copyright (c) 1989 Mark H. Colburn.
+ * 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 Mark H. Colburn and sponsored by The USENIX Association.
+ *
+ * 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 _PAX_H
+#define _PAX_H
+
+/* Headers */
+
+#include "config.h"
+#include "limits.h"
+#include <stdio.h>
+#ifdef __STDC__ /* Xn */
+# include <stdlib.h> /* Xn */
+#endif /* Xn */
+#include <errno.h>
+#include <signal.h>
+#include <ctype.h>
+#include <sys/types.h>
+#ifdef _POSIX_SOURCE /* Xn */
+# include <utime.h> /* Xn */
+# include <unistd.h> /* Xn */
+#else /* Xn */
+# include <sys/ioctl.h> /* Xn */
+#endif /* Xn */
+#include <sys/stat.h>
+#include "regexp.h"
+
+#if defined(DIRENT) || defined(_POSIX_SOURCE)
+# ifdef PAXDIR
+# include "paxdir.h"
+# else
+# include <dirent.h>
+# endif
+#else
+# ifdef hpux
+# include <ndir.h>
+# else
+# ifdef XENIX_286
+# include <sys/ndir.h>
+# else /* XENIX_286 */
+# include <sys/dir.h>
+# endif /* XENIX_286 */
+# endif /* hpux */
+# define dirent direct
+#endif
+
+#ifndef major
+# include <sys/sysmacros.h>
+#endif /* major */
+
+#ifdef SYSTIME
+# include <sys/time.h>
+#else /* SYSTIME */
+# include <time.h>
+#endif /* SYSTIME */
+
+#ifndef V7
+# include <fcntl.h>
+#endif
+
+#ifdef XENIX
+# include <sys/inode.h>
+#endif
+#ifdef XENIX_286
+#include <sys/param.h>
+#endif /* XENIX_286 */
+
+#include <pwd.h>
+#include <grp.h>
+#ifndef XENIX_286
+# ifndef _POSIX_SOURCE /* Xn */
+# include <sys/file.h> /* Xn */
+# endif /* _POSIX_SOURCE */ /* Xn */
+#endif /* XENIX_286 */
+
+/* Defines */
+
+#ifdef _POSIX_SOURCE /* Xn */
+# define STDIN STDIN_FILENO /* Standard input file descriptor */ /* Xn */
+# define STDOUT STDOUT_FILENO /* Standard output file descriptor */ /* Xn */
+#else /* Xn */
+# define STDIN 0 /* Standard input file descriptor */ /* Xn */
+# define STDOUT 1 /* Standard output file descriptor */ /* Xn */
+#endif /* Xn */
+
+/*
+ * Open modes; there is no <fcntl.h> with v7 UNIX and other versions of
+ * UNIX may not have all of these defined...
+ */
+
+#ifndef O_RDONLY
+# define O_RDONLY 0
+#endif
+
+#ifndef O_WRONLY
+# define O_WRONLY 1
+#endif
+
+#ifndef O_RDWR
+# define O_RDWR 2 /* Xn */
+#endif
+
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif
+ /* Xn */
+/* Xn
+ * Lseek symbolic constants; there is no <unistd.h> with most UNIXes, so Xn
+ * all of these may not be defined... Xn
+ */ /* Xn */
+ /* Xn */
+#ifndef SEEK_SET /* Xn */
+# define SEEK_SET 0 /* Xn */
+#endif /* Xn */
+ /* Xn */
+#ifndef SEEK_CUR /* Xn */
+# define SEEK_CUR 1 /* Xn */
+#endif /* Xn */
+ /* Xn */
+#ifndef SEEK_END /* Xn */
+# define SEEK_END 2 /* Xn */
+#endif /* Xn */
+
+#ifndef NULL
+# ifdef __STDC__ /* Xn */
+# define NULL ((void *) 0) /* Xn */
+# else /* Xn */
+# define NULL ((char *) 0) /* Xn */
+# endif /* Xn */
+#endif
+
+#define TMAGIC "ustar" /* ustar and a null */
+#define TMAGLEN 6
+#define TVERSION "00" /* 00 and no null */
+#define TVERSLEN 2
+
+/* Values used in typeflag field */
+#define REGTYPE '0' /* Regular File */
+#define AREGTYPE '\0' /* Regular File */
+#define LNKTYPE '1' /* Link */
+#define SYMTYPE '2' /* Reserved */
+#define CHRTYPE '3' /* Character Special File */
+#define BLKTYPE '4' /* Block Special File */
+#define DIRTYPE '5' /* Directory */
+#define FIFOTYPE '6' /* FIFO */
+#define CONTTYPE '7' /* Reserved */
+#define SOCKTYPE 'S' /* Socket */ /* Xn */
+
+#define BLOCKSIZE 512 /* all output is padded to 512 bytes */
+#define uint unsigned int /* Not always in types.h */
+#define ushort unsigned short /* Not always in types.h */
+#define BLOCK 5120 /* Default archive block size */
+#define H_COUNT 10 /* Number of items in ASCII header */
+#define H_PRINT "%06o%06o%06o%06o%06o%06o%06o%011lo%06o%011lo"
+#define H_SCAN "%6ho%6ho%6ho%6ho%6ho%6ho%6ho%11lo%6o%11lo"
+#define H_STRLEN 70 /* ASCII header string length */
+#define M_ASCII "070707" /* ASCII magic number */
+#define M_BINARY 070707 /* Binary magic number */
+#define M_STRLEN 6 /* ASCII magic number length */
+#define PATHELEM 256 /* Pathname element count limit */
+#define S_IFSHF 12 /* File type shift (shb in stat.h) */
+#ifndef S_IPERM /* Xn */
+# define S_IPERM 07777 /* File permission bits (shb in stat.h) */ /* Xn */
+#endif /* Xn */
+#define S_IPEXE 07000 /* Special execution bits (shb in stat.h) */
+#define S_IPOPN 0777 /* Open access bits (shb in stat.h) */
+
+/*
+ * Trailer pathnames. All must be of the same length.
+ */
+#define TRAILER "TRAILER!!!" /* Archive trailer (cpio compatible) */
+#define TRAILZ 11 /* Trailer pathname length (including null) */
+
+#include "port.h"
+
+
+#define TAR 1
+#define CPIO 2
+#define PAX 3
+
+#define AR_READ 0
+#define AR_WRITE 1
+#define AR_EXTRACT 2
+#define AR_APPEND 4
+
+/*
+ * Header block on tape.
+ */
+#define NAMSIZ 100
+#define PFIXSIZ 155
+#define TUNMLEN 32
+#define TGNMLEN 32
+
+/* The checksum field is filled with this while the checksum is computed. */
+#define CHKBLANKS " " /* 8 blanks, no null */
+
+/*
+ * Exit codes from the "tar" program
+ */
+#define EX_SUCCESS 0 /* success! */
+#define EX_ARGSBAD 1 /* invalid args */
+#define EX_BADFILE 2 /* invalid filename */
+#define EX_BADARCH 3 /* bad archive */
+#define EX_SYSTEM 4 /* system gave unexpected error */
+
+#define ROUNDUP(a,b) (((a) % (b)) == 0 ? (a) : ((a) + ((b) - ((a) % (b)))))
+
+/*
+ * Mininum value.
+ */
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+
+/*
+ * Remove a file or directory.
+ */
+#define REMOVE(name, asb) \
+ (((asb)->sb_mode & S_IFMT) == S_IFDIR ? rmdir(name) : unlink(name))
+
+/*
+ * Cast and reduce to unsigned short.
+ */
+#define USH(n) ((n) & 0777777)
+
+
+/* Type Definitions */
+
+/*
+ * Binary archive header (obsolete).
+ */
+typedef struct {
+ short b_dev; /* Device code */
+ ushort b_ino; /* Inode number */
+ ushort b_mode; /* Type and permissions */
+ ushort b_uid; /* Owner */
+ ushort b_gid; /* Group */
+ short b_nlink; /* Number of links */
+ short b_rdev; /* Real device */
+ ushort b_mtime[2]; /* Modification time (hi/lo) */
+ ushort b_name; /* Length of pathname (with null) */
+ ushort b_size[2]; /* Length of data */
+} Binary;
+
+/*
+ * File status with symbolic links. Kludged to hold symbolic link pathname
+ * within structure.
+ */
+typedef struct {
+ struct stat sb_stat;
+ char sb_link[PATH_MAX + 1];
+} Stat;
+
+#define STAT(name, asb) stat(name, &(asb)->sb_stat)
+#define FSTAT(fd, asb) fstat(fd, &(asb)->sb_stat)
+
+#define sb_dev sb_stat.st_dev
+#define sb_ino sb_stat.st_ino
+#define sb_mode sb_stat.st_mode
+#define sb_nlink sb_stat.st_nlink
+#define sb_uid sb_stat.st_uid
+#define sb_gid sb_stat.st_gid
+#define sb_rdev sb_stat.st_rdev
+#define sb_size sb_stat.st_size
+#define sb_atime sb_stat.st_atime
+#define sb_mtime sb_stat.st_mtime
+#define sb_ctime sb_stat.st_ctime
+
+#ifdef S_IFLNK
+# define LSTAT(name, asb) lstat(name, &(asb)->sb_stat)
+# define sb_blksize sb_stat.st_blksize
+# define sb_blocks sb_stat.st_blocks
+#else /* S_IFLNK */
+/*
+ * File status without symbolic links.
+ */
+# define LSTAT(name, asb) stat(name, &(asb)->sb_stat)
+#endif /* S_IFLNK */
+
+/*
+ * Hard link sources. One or more are chained from each link structure.
+ */
+typedef struct name {
+ struct name *p_forw; /* Forward chain (terminated) */
+ struct name *p_back; /* Backward chain (circular) */
+ char *p_name; /* Pathname to link from */
+} Path;
+
+/*
+ * File linking information. One entry exists for each unique file with with
+ * outstanding hard links.
+ */
+typedef struct link {
+ struct link *l_forw; /* Forward chain (terminated) */
+ struct link *l_back; /* Backward chain (terminated) */
+ dev_t l_dev; /* Device */
+ ino_t l_ino; /* Inode */
+ ushort l_nlink; /* Unresolved link count */
+ OFFSET l_size; /* Length */
+ char *l_name; /* pathname to link from */
+ Path *l_path; /* Pathname which link to l_name */
+} Link;
+
+/*
+ * Structure for ed-style replacement strings (-s option).
+*/
+typedef struct replstr {
+ regexp *comp; /* compiled regular expression */
+ char *replace; /* replacement string */
+ char print; /* >0 if we are to print replacement */
+ char global; /* >0 if we are to replace globally */
+ struct replstr *next; /* pointer to next record */
+} Replstr;
+
+
+/*
+ * This has to be included here to insure that all of the type
+ * delcarations are declared for the prototypes.
+ */
+#include "func.h"
+
+
+#ifndef NO_EXTERN
+/* Globally Available Identifiers */
+
+extern char *ar_file;
+extern char *bufend;
+extern char *bufstart;
+extern char *bufidx;
+extern char *myname;
+extern int archivefd;
+extern int blocking;
+extern uint blocksize;
+extern int gid;
+extern int head_standard;
+extern int ar_interface;
+extern int ar_format;
+extern int mask;
+extern int ttyf;
+extern int uid;
+extern OFFSET total;
+extern short areof;
+extern short f_append;
+extern short f_create;
+extern short f_extract;
+extern short f_follow_links;
+extern short f_interactive;
+extern short f_linksleft;
+extern short f_list;
+extern short f_modified;
+extern short f_verbose;
+extern short f_link;
+extern short f_owner;
+extern short f_access_time;
+extern short f_pass;
+extern short f_quiet; /* Xn */
+extern short f_disposition;
+extern short f_reverse_match;
+extern short f_mtime;
+extern short f_dir_create;
+extern short f_unconditional;
+extern short f_newer;
+extern time_t now;
+extern uint arvolume;
+extern int names_from_stdin;
+extern Replstr *rplhead;
+extern Replstr *rpltail;
+extern char **n_argv;
+extern int n_argc;
+extern FILE *msgfile;
+#endif /* NO_EXTERN */
+
+extern char *optarg;
+extern int optind;
+extern int sys_nerr;
+extern char *sys_errlist[];
+#ifndef _POSIX_SOURCE /* Xn */
+extern int errno;
+#endif /* Xn */
+
+#endif /* _PAX_H */
diff --git a/private/posix/programs/pax/pax.rc b/private/posix/programs/pax/pax.rc
new file mode 100644
index 000000000..1817563ec
--- /dev/null
+++ b/private/posix/programs/pax/pax.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Posix Archive Application"
+#define VER_INTERNALNAME_STR "pax.exe"
+
+#include "common.ver"
+
+
diff --git a/private/posix/programs/pax/paxdir.c b/private/posix/programs/pax/paxdir.c
new file mode 100644
index 000000000..3d3f182db
--- /dev/null
+++ b/private/posix/programs/pax/paxdir.c
@@ -0,0 +1,713 @@
+/*
+ opendir -- open a directory stream
+
+ last edit: 16-Jun-1987 D A Gwyn
+*/
+
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "paxdir.h"
+
+#ifdef BSD_SYSV
+/*
+ <sys/_dir.h> -- definitions for 4.2,4.3BSD directories
+
+ last edit: 25-Apr-1987 D A Gwyn
+
+ A directory consists of some number of blocks of DIRBLKSIZ bytes each,
+ where DIRBLKSIZ is chosen such that it can be transferred to disk in a
+ single atomic operation (e.g., 512 bytes on most machines).
+
+ Each DIRBLKSIZ-byte block contains some number of directory entry
+ structures, which are of variable length. Each directory entry has the
+ beginning of a (struct direct) at the front of it, containing its
+ filesystem-unique ident number, the length of the entry, and the length
+ of the name contained in the entry. These are followed by the NUL-
+ terminated name padded to a (long) boundary with 0 bytes. The maximum
+ length of a name in a directory is MAXNAMELEN.
+
+ The macro DIRSIZ(dp) gives the amount of space required to represent a
+ directory entry. Free space in a directory is represented by entries
+ that have dp->d_reclen > DIRSIZ(dp). All DIRBLKSIZ bytes in a
+ directory block are claimed by the directory entries; this usually
+ results in the last entry in a directory having a large dp->d_reclen.
+ When entries are deleted from a directory, the space is returned to the
+ previous entry in the same directory block by increasing its
+ dp->d_reclen. If the first entry of a directory block is free, then
+ its dp->d_fileno is set to 0; entries other than the first in a
+ directory do not normally have dp->d_fileno set to 0.
+
+ prerequisite: <sys/types.h>
+*/
+
+#if defined(accel) || defined(sun) || defined(vax)
+#define DIRBLKSIZ 512 /* size of directory block */
+#else
+#ifdef alliant
+#define DIRBLKSIZ 4096 /* size of directory block */
+#else
+#ifdef gould
+#define DIRBLKSIZ 1024 /* size of directory block */
+#else
+#ifdef ns32000 /* Dynix System V */
+#define DIRBLKSIZ 2600 /* size of directory block */
+#else /* be conservative; multiple blocks are okay
+ * but fractions are not */
+#define DIRBLKSIZ 4096 /* size of directory block */
+#endif
+#endif
+#endif
+#endif
+
+#define MAXNAMELEN 255 /* maximum filename length */
+/* NOTE: not MAXNAMLEN, which has been preempted by SVR3 <dirent.h> */
+
+struct direct { /* data from read()/_getdirentries() */
+ unsigned long d_fileno; /* unique ident of entry */
+ unsigned short d_reclen; /* length of this record */
+ unsigned short d_namlen; /* length of string in d_name */
+ char d_name[MAXNAMELEN + 1]; /* NUL-terminated filename */
+};
+
+/*
+ The DIRSIZ macro gives the minimum record length which will hold the
+ directory entry. This requires the amount of space in a (struct
+ direct) without the d_name field, plus enough space for the name with a
+ terminating NUL character, rounded up to a (long) boundary.
+
+ (Note that Berkeley didn't properly compensate for struct padding,
+ but we nevertheless have to use the same size as the actual system.)
+*/
+
+#define DIRSIZ( dp ) ((sizeof(struct direct) - (MAXNAMELEN+1) \
+ + sizeof(long) + (dp)->d_namlen) \
+ / sizeof(long) * sizeof(long))
+
+#else
+#ifndef _POSIX_SOURCE
+#include <sys/dirent.h>
+#endif
+#ifdef SYSV3
+#undef MAXNAMLEN /* avoid conflict with SVR3 */
+#endif
+ /* Good thing we don't need to use the DIRSIZ() macro! */
+#ifdef d_ino /* 4.3BSD/NFS using d_fileno */
+#undef d_ino /* (not absolutely necessary) */
+#else
+#define d_fileno d_ino /* (struct direct) member */
+#endif
+#endif
+#ifdef UNK
+#ifndef UFS
+#include "***** ERROR ***** UNK applies only to UFS"
+/* One could do something similar for getdirentries(), but I didn't bother. */
+#endif
+#include <signal.h>
+#endif
+
+#if defined(UFS) + defined(BFS) + defined(NFS) != 1 /* sanity check */
+#include "***** ERROR ***** exactly one of UFS, BFS, or NFS must be defined"
+#endif
+
+#ifdef UFS
+#define RecLen( dp ) (sizeof(struct direct)) /* fixed-length entries */
+#else /* BFS || NFS */
+#define RecLen( dp ) ((dp)->d_reclen) /* variable-length entries */
+#endif
+
+#ifdef NFS
+#ifdef BSD_SYSV
+#define getdirentries _getdirentries /* package hides this system call */
+#endif
+extern int getdirentries();
+static long dummy; /* getdirentries() needs basep */
+#define GetBlock( fd, buf, n ) getdirentries( fd, buf, (unsigned)n, &dummy )
+#else /* UFS || BFS */
+#ifdef BSD_SYSV
+#define read _read /* avoid emulation overhead */
+#endif
+extern int read();
+#define GetBlock( fd, buf, n ) read( fd, buf, (unsigned)n )
+#endif
+
+#ifdef UNK
+extern int _getdents(); /* actual system call */
+#endif
+
+extern char *strncpy();
+extern int fstat();
+extern OFFSET lseek();
+
+extern int errno;
+
+#ifndef DIRBLKSIZ
+#define DIRBLKSIZ 4096 /* directory file read buffer size */
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef SEEK_CUR
+#define SEEK_CUR 1
+#endif
+
+#ifndef S_ISDIR /* macro to test for directory file */
+#define S_ISDIR( mode ) (((mode) & S_IFMT) == S_IFDIR)
+#endif
+
+
+#ifndef SEEK_CUR
+#define SEEK_CUR 1
+#endif
+
+#ifdef BSD_SYSV
+#define open _open /* avoid emulation overhead */
+#endif
+
+extern int getdents(); /* SVR3 system call, or emulation */
+
+typedef char *pointer; /* (void *) if you have it */
+
+extern void free();
+extern pointer malloc();
+extern int
+open(), close(), fstat();
+
+extern int errno;
+extern OFFSET lseek();
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+
+typedef int bool; /* Boolean data type */
+#define false 0
+#define true 1
+
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef O_RDONLY
+#define O_RDONLY 0
+#endif
+
+#ifndef S_ISDIR /* macro to test for directory file */
+#define S_ISDIR( mode ) (((mode) & S_IFMT) == S_IFDIR)
+#endif
+
+#ifdef __STDC__
+
+DIR *opendir(char *dirname)
+
+#else
+
+DIR *opendir(dirname)
+char *dirname; /* name of directory */
+
+#endif
+{
+ register DIR *dirp; /* -> malloc'ed storage */
+ register int fd; /* file descriptor for read */
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: + sizeof() in paxdir.c\n");
+#endif
+ struct stat sbuf; /* result of fstat() */
+
+ if ((fd = open(dirname, O_RDONLY)) < 0)
+ return ((DIR *)NULL); /* errno set by open() */
+
+ if (fstat(fd, &sbuf) != 0 || !S_ISDIR(sbuf.st_mode)) {
+ close(fd);
+ errno = ENOTDIR;
+ return ((DIR *)NULL); /* not a directory */
+ }
+ if ((dirp = (DIR *) malloc(sizeof(DIR))) == (DIR *)NULL
+ || (dirp->dd_buf = (char *) malloc((unsigned) DIRBUF)) == (char *)NULL
+ ) {
+ register int serrno = errno;
+ /* errno set to ENOMEM by sbrk() */
+
+ if (dirp != (DIR *)NULL)
+ free((pointer) dirp);
+
+ close(fd);
+ errno = serrno;
+ return ((DIR *)NULL); /* not enough memory */
+ }
+ dirp->dd_fd = fd;
+ dirp->dd_loc = dirp->dd_size = 0; /* refill needed */
+
+ return dirp;
+}
+
+
+/*
+ * closedir -- close a directory stream
+ *
+ * last edit: 11-Nov-1988 D A Gwyn
+ */
+
+#ifdef __STDC__
+
+int closedir(register DIR *dirp)
+
+#else
+
+int closedir(dirp)
+register DIR *dirp; /* stream from opendir() */
+
+#endif
+{
+ register int fd;
+
+ if ( dirp == (DIR *)NULL || dirp->dd_buf == (char *)NULL ) {
+ errno = EFAULT;
+ return -1; /* invalid pointer */
+ }
+
+ fd = dirp->dd_fd; /* bug fix thanks to R. Salz */
+ free( (pointer)dirp->dd_buf );
+ free( (pointer)dirp );
+ return close( fd );
+}
+
+
+/*
+ readdir -- read next entry from a directory stream
+
+ last edit: 25-Apr-1987 D A Gwyn
+*/
+
+#ifdef __STDC__
+
+struct dirent *readdir(register DIR *dirp)
+
+#else
+
+struct dirent *readdir(dirp)
+register DIR *dirp; /* stream from opendir() */
+
+#endif
+{
+ register struct dirent *dp; /* -> directory data */
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: struct dirent *readdir() in paxdir.c\n");
+#endif
+ if (dirp == (DIR *)NULL || dirp->dd_buf == (char *)NULL) {
+ errno = EFAULT;
+ return (struct dirent *)NULL; /* invalid pointer */
+ }
+ do {
+ if (dirp->dd_loc >= dirp->dd_size) /* empty or obsolete */
+ dirp->dd_loc = dirp->dd_size = 0;
+
+ if (dirp->dd_size == 0 /* need to refill buffer */
+ && (dirp->dd_size =
+ getdents(dirp->dd_fd, dirp->dd_buf, (unsigned) DIRBUF)
+ ) <= 0
+ )
+ return ((struct dirent *)NULL); /* EOF or error */
+
+ dp = (struct dirent *) & dirp->dd_buf[dirp->dd_loc];
+ dirp->dd_loc += dp->d_reclen;
+ }
+ while (dp->d_ino == 0L); /* don't rely on getdents() */
+
+ return dp;
+}
+
+
+/*
+ seekdir -- reposition a directory stream
+
+ last edit: 24-May-1987 D A Gwyn
+
+ An unsuccessful seekdir() will in general alter the current
+ directory position; beware.
+
+ NOTE: 4.nBSD directory compaction makes seekdir() & telldir()
+ practically impossible to do right. Avoid using them!
+*/
+
+#ifdef __STDC__
+
+void seekdir(register DIR *dirp, register OFFSET loc)
+
+#else
+
+void seekdir(dirp, loc)
+register DIR *dirp; /* stream from opendir() */
+register OFFSET loc; /* position from telldir() */
+
+#endif
+{
+ register bool rewind; /* "start over when stymied" flag */
+
+ if (dirp == (DIR *)NULL || dirp->dd_buf == (char *)NULL) {
+ errno = EFAULT;
+ return; /* invalid pointer */
+ }
+ /*
+ * A (struct dirent)'s d_off is an invented quantity on 4.nBSD
+ * NFS-supporting systems, so it is not safe to lseek() to it.
+ */
+
+ /* Monotonicity of d_off is heavily exploited in the following. */
+
+ /*
+ * This algorithm is tuned for modest directory sizes. For huge
+ * directories, it might be more efficient to read blocks until the first
+ * d_off is too large, then back up one block, or even to use binary
+ * search on the directory blocks. I doubt that the extra code for that
+ * would be worthwhile.
+ */
+
+ if (dirp->dd_loc >= dirp->dd_size /* invalid index */
+ || ((struct dirent *) & dirp->dd_buf[dirp->dd_loc])->d_off > loc
+ /* too far along in buffer */
+ )
+ dirp->dd_loc = 0; /* reset to beginning of buffer */
+ /* else save time by starting at current dirp->dd_loc */
+
+ for (rewind = true;;) {
+ register struct dirent *dp;
+
+ /* See whether the matching entry is in the current buffer. */
+
+ if ((dirp->dd_loc < dirp->dd_size /* valid index */
+ || readdir(dirp) != (struct dirent *)NULL /* next buffer read */
+ && (dirp->dd_loc = 0, true) /* beginning of buffer set */
+ )
+ && (dp = (struct dirent *) & dirp->dd_buf[dirp->dd_loc])->d_off
+ <= loc /* match possible in this buffer */
+ ) {
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: || readdir() in paxdir.c\n");
+#endif
+ for ( /* dp initialized above */ ;
+ (char *) dp < &dirp->dd_buf[dirp->dd_size];
+ dp = (struct dirent *) ((char *) dp + dp->d_reclen)
+ )
+ if (dp->d_off == loc) { /* found it! */
+ dirp->dd_loc =
+ (char *) dp - dirp->dd_buf;
+ return;
+ }
+ rewind = false; /* no point in backing up later */
+ dirp->dd_loc = dirp->dd_size; /* set end of buffer */
+ } else
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: dp = () in paxdir.c\n");
+#endif
+ /* whole buffer past matching entry */ if (!rewind) { /* no point in searching
+ * further */
+ errno = EINVAL;
+ return; /* no entry at specified loc */
+ } else { /* rewind directory and start over */
+ rewind = false; /* but only once! */
+
+ dirp->dd_loc = dirp->dd_size = 0;
+
+ if (lseek(dirp->dd_fd, (OFFSET) 0, SEEK_SET)
+ != 0
+ )
+ return; /* errno already set (EBADF) */
+
+ if (loc == 0)
+ return; /* save time */
+ }
+ }
+}
+
+
+/* telldir - report directory stream position
+ *
+ * DESCRIPTION
+ *
+ * Returns the offset of the next directory entry in the
+ * directory associated with dirp.
+ *
+ * NOTE: 4.nBSD directory compaction makes seekdir() & telldir()
+ * practically impossible to do right. Avoid using them!
+ *
+ * PARAMETERS
+ *
+ * DIR *dirp - stream from opendir()
+ *
+ * RETURNS
+ *
+ * Return offset of next entry
+ */
+
+
+#ifdef __STDC__
+
+OFFSET telldir(DIR *dirp)
+
+#else
+
+OFFSET telldir(dirp)
+DIR *dirp; /* stream from opendir() */
+
+#endif
+{
+ if (dirp == (DIR *)NULL || dirp->dd_buf == (char *)NULL) {
+ errno = EFAULT;
+ return -1; /* invalid pointer */
+ }
+ if (dirp->dd_loc < dirp->dd_size) /* valid index */
+ return ((struct dirent *) & dirp->dd_buf[dirp->dd_loc])->d_off;
+ else /* beginning of next directory block */
+ return lseek(dirp->dd_fd, (OFFSET) 0, SEEK_CUR);
+}
+
+
+#ifdef UFS
+
+/*
+ The following routine is necessary to handle DIRSIZ-long entry names.
+ Thanks to Richard Todd for pointing this out.
+*/
+
+
+/* return # chars in embedded name */
+
+#ifdef __STDC__
+
+static int NameLen(char *name)
+
+#else
+
+static int NameLen(name)
+char *name; /* -> name embedded in struct direct */
+
+#endif
+{
+ register char *s; /* -> name[.] */
+ register char *stop = &name[DIRSIZ]; /* -> past end of name field */
+
+ for (s = &name[1]; /* (empty names are impossible) */
+ *s != '\0' /* not NUL terminator */
+ && ++s < stop; /* < DIRSIZ characters scanned */
+ );
+
+ return s - name; /* # valid characters in name */
+}
+
+#else /* BFS || NFS */
+
+extern int strlen();
+
+#define NameLen( name ) strlen( name ) /* names are always NUL-terminated */
+
+#endif
+
+#ifdef UNK
+static enum {
+ maybe, no, yes
+} state = maybe;
+
+
+/* sig_catch - used to catch signals
+ *
+ * DESCRIPTION
+ *
+ * Used to catch signals.
+ */
+
+/*ARGSUSED*/
+
+#ifdef __STDC__
+
+static void sig_catch(int sig)
+
+#else
+
+static void sig_catch(sig)
+int sig; /* must be SIGSYS */
+
+#endif
+{
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static void sig_catch() in paxdir.c\n");
+#endif
+ state = no; /* attempted _getdents() faulted */
+}
+#endif
+
+
+/* getdents - get directory entries
+ *
+ * DESCRIPTION
+ *
+ * Gets directory entries from the filesystem in an implemenation
+ * defined way.
+ *
+ * PARAMETERS
+ *
+ * int fildes - directory file descriptor
+ * char *buf - where to put the (struct dirent)s
+ * unsigned nbyte - size of buf[]
+ *
+ * RETURNS
+ *
+ * Returns number of bytes read; 0 on EOF, -1 on error
+ */
+
+#ifdef __STDC__
+
+int getdents(int fildes, char *buf, unsigned nbyte)
+
+#else
+
+int getdents(fildes, buf, nbyte)
+int fildes; /* directory file descriptor */
+char *buf; /* where to put the (struct dirent)s */
+unsigned nbyte; /* size of buf[] */
+
+#endif
+{
+ int serrno; /* entry errno */
+ OFFSET offset; /* initial directory file offset */
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int getdents() in paxdir.c\n");
+#endif
+ struct stat statb; /* fstat() info */
+ union {
+ /* directory file block buffer */
+#ifdef UFS
+ char dblk[DIRBLKSIZ + 1];
+#else
+ char dblk[DIRBLKSIZ];
+#endif
+ struct direct dummy; /* just for alignment */
+ } u; /* (avoids having to malloc()) */
+ register struct direct *dp; /* -> u.dblk[.] */
+ register struct dirent *bp; /* -> buf[.] */
+
+#ifdef UNK
+ switch (state) {
+ SIG_T (*shdlr)(); /* entry SIGSYS handler */
+ register int retval; /* return from _getdents() if any */
+
+ case yes: /* _getdents() is known to work */
+ return _getdents(fildes, buf, nbyte);
+
+ case maybe: /* first time only */
+ shdlr = signal(SIGSYS, sig_catch);
+ retval = _getdents(fildes, buf, nbyte); /* try it */
+ signal(SIGSYS, shdlr);
+
+ if (state == maybe) { /* SIGSYS did not occur */
+ state = yes; /* so _getdents() must have worked */
+ return retval;
+ }
+ /* else fall through into emulation */
+
+/* case no: /* fall through into emulation */
+ }
+#endif
+
+ if (buf == (char *)NULL
+#ifdef ATT_SPEC
+ || (unsigned long) buf % sizeof(long) != 0 /* ugh */
+#endif
+ ) {
+ errno = EFAULT; /* invalid pointer */
+ return -1;
+ }
+ if (fstat(fildes, &statb) != 0) {
+ return -1; /* errno set by fstat() */
+ }
+
+ if (!S_ISDIR(statb.st_mode)) {
+ errno = ENOTDIR; /* not a directory */
+ return -1;
+ }
+ if ((offset = lseek(fildes, (OFFSET) 0, SEEK_CUR)) < 0) {
+ return -1; /* errno set by lseek() */
+ }
+
+#ifdef BFS /* no telling what remote hosts do */
+ if ((unsigned long) offset % DIRBLKSIZ != 0) {
+ errno = ENOENT; /* file pointer probably misaligned */
+ return -1;
+ }
+#endif
+
+ serrno = errno; /* save entry errno */
+
+ for (bp = (struct dirent *) buf; bp == (struct dirent *) buf;) {
+
+ /* convert next directory block */
+ int size;
+
+ do {
+ size = GetBlock(fildes, u.dblk, DIRBLKSIZ);
+ } while (size == -1 && errno == EINTR);
+
+ if (size <= 0) {
+ return size; /* EOF or error (EBADF) */
+ }
+
+ for (dp = (struct direct *) u.dblk;
+ (char *) dp < &u.dblk[size];
+ dp = (struct direct *) ((char *) dp + RecLen(dp))
+ ) {
+#ifndef UFS
+ if (dp->d_reclen <= 0) {
+ errno = EIO; /* corrupted directory */
+ return -1;
+ }
+#endif
+
+ if (dp->d_fileno != 0) { /* non-empty; copy to user buffer */
+ register int reclen =
+ DIRENTSIZ(NameLen(dp->d_name));
+
+ if ((char *) bp + reclen > &buf[nbyte]) {
+ errno = EINVAL;
+ return -1; /* buf too small */
+ }
+ bp->d_ino = dp->d_fileno;
+ bp->d_off = offset + ((char *) dp - u.dblk);
+ bp->d_reclen = reclen;
+
+ {
+#ifdef UFS
+ /* Is the following kludge ugly? You bet. */
+
+ register char save = dp->d_name[DIRSIZ];
+ /* save original data */
+
+ dp->d_name[DIRSIZ] = '\0';
+ /* ensure NUL termination */
+#endif
+ /* adds NUL padding */
+ strncpy(bp->d_name, dp->d_name, reclen - DIRENTBASESIZ);
+#ifdef UFS
+ dp->d_name[DIRSIZ] = save;
+ /* restore original data */
+#endif
+ }
+
+ bp = (struct dirent *) ((char *) bp + reclen);
+ }
+ }
+
+#ifndef BFS /* 4.2BSD screwed up; fixed in 4.3BSD */
+ if ((char *) dp > &u.dblk[size]) {
+ errno = EIO; /* corrupted directory */
+ return -1;
+ }
+#endif
+ }
+
+ errno = serrno; /* restore entry errno */
+ return (char *) bp - buf; /* return # bytes read */
+}
diff --git a/private/posix/programs/pax/paxdir.h b/private/posix/programs/pax/paxdir.h
new file mode 100644
index 000000000..84b4f08d9
--- /dev/null
+++ b/private/posix/programs/pax/paxdir.h
@@ -0,0 +1,68 @@
+/*
+ <dirent.h> -- definitions for SVR3 directory access routines
+
+ last edit: 25-Apr-1987 D A Gwyn
+
+ Prerequisite: <sys/types.h>
+*/
+
+#ifndef _PAX_DIRENT_H
+#define _PAX_DIRENT_H
+
+#include "config.h"
+#ifdef USG
+#define UFS
+#else
+#ifdef BSD
+#define BFS
+#endif
+#endif
+
+struct dirent { /* data from getdents()/readdir() */
+ long d_ino; /* inode number of entry */
+ off_t d_off; /* offset of disk directory entry */
+ unsigned short d_reclen; /* length of this record */
+ char d_name[1]; /* name of file (non-POSIX) */
+};
+
+typedef struct {
+ int dd_fd; /* file descriptor */
+ int dd_loc; /* offset in block */
+ int dd_size; /* amount of valid data */
+ char *dd_buf; /* -> directory block */
+} DIR; /* stream data from opendir() */
+
+
+/*
+ * The following nonportable ugliness could have been avoided by defining
+ * DIRENTSIZ and DIRENTBASESIZ to also have (struct dirent *) arguments.
+ */
+#define DIRENTBASESIZ (((struct dirent *)0)->d_name \
+ - (char *)&((struct dirent *)0)->d_ino)
+#define DIRENTSIZ( namlen ) ((DIRENTBASESIZ + sizeof(long) + (namlen)) \
+ / sizeof(long) * sizeof(long))
+
+#define MAXNAMLEN 512 /* maximum filename length */
+
+#ifndef NAME_MAX
+#define NAME_MAX (MAXNAMLEN - 1) /* DAG -- added for POSIX */
+#endif
+
+#define DIRBUF 8192 /* buffer size for fs-indep. dirs */
+ /* must in general be larger than the filesystem buffer size */
+
+#ifdef __STDC__ /* Xn */
+extern DIR *opendir(char *);
+extern struct dirent *readdir(DIR *);
+extern OFFSET telldir(DIR *);
+extern void seekdir(DIR *, OFFSET);
+extern int closedir(DIR *);
+#else /* Xn */
+extern DIR *opendir();
+extern struct dirent *readdir();
+extern OFFSET telldir();
+extern void seekdir();
+extern int closedir();
+#endif /* Xn */
+
+#endif /* _PAX_DIRENT_H */
diff --git a/private/posix/programs/pax/port.c b/private/posix/programs/pax/port.c
new file mode 100644
index 000000000..47dc95e52
--- /dev/null
+++ b/private/posix/programs/pax/port.c
@@ -0,0 +1,205 @@
+/* $Source: /u/mark/src/pax/RCS/port.c,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * port.c - These are routines not available in all environments.
+ *
+ * DESCRIPTION
+ *
+ * The routines contained in this file are provided for portability to
+ * other versions of UNIX or other operating systems (e.g. MSDOS).
+ * Not all systems have the same functions or the same semantics,
+ * these routines attempt to bridge the gap as much as possible.
+ *
+ * AUTHOR
+ *
+ * Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
+ * John Gilmore (gnu@hoptoad)
+ *
+ * Sponsored by The USENIX Association for public distribution.
+ *
+ * Copyright (c) 1989 Mark H. Colburn.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice is 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 Mark H. Colburn and sponsored by The
+ * USENIX Association.
+ *
+ * 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.
+ *
+ * $Log: port.c,v $
+ * Revision 1.2 89/02/12 10:05:35 mark
+ * 1.2 release fixes
+ *
+ * Revision 1.1 88/12/23 18:02:29 mark
+ * Initial revision
+ *
+ */
+
+#ifndef lint
+static char *ident = "$Id: port.c,v 1.2 89/02/12 10:05:35 mark Exp $";
+static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n";
+#endif /* ! lint */
+
+
+/* Headers */
+
+#include "pax.h"
+#ifdef _POSIX_SOURCE /* Xn */
+# include <sys/wait.h> /* Xn */
+#endif /* Xn */
+
+
+/*
+ * Some computers are not so crass as to align themselves into the BSD or USG
+ * camps. If a system supplies all of the routines we fake here, add it to
+ * the list in the #if !defined()'s below and it'll all be skipped.
+ */
+
+#if !defined(_POSIX_SOURCE) && !defined(mc300) && !defined(mc500) && !defined(mc700) && !defined(BSD) /* Xn */
+
+/* mkdir - make a directory
+ *
+ * DESCRIPTION
+ *
+ * Mkdir will make a directory of the name "dpath" with a mode of
+ * "dmode". This is consistent with the BSD mkdir() function and the
+ * P1003.1 definitions of MKDIR.
+ *
+ * PARAMETERS
+ *
+ * dpath - name of directory to create
+ * dmode - mode of the directory
+ *
+ * RETURNS
+ *
+ * Returns 0 if the directory was successfully created, otherwise a
+ * non-zero return value will be passed back to the calling function
+ * and the value of errno should reflect the error.
+ */
+
+#ifdef __STDC__
+
+int mkdir(char *dpath, int dmode)
+
+#else
+
+int mkdir(dpath, dmode)
+char *dpath;
+int dmode;
+
+#endif
+{
+ int cpid, status;
+ Stat statbuf;
+ extern int errno;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int mkdir() in port.c\n");
+#endif
+ if (STAT(dpath, &statbuf) == 0) {
+ errno = EEXIST; /* Stat worked, so it already exists */
+ return (-1);
+ }
+ /* If stat fails for a reason other than non-existence, return error */
+ if (errno != ENOENT)
+ return (-1);
+
+puts("b fork1");
+ switch (cpid = fork()) {
+
+ case -1: /* Error in fork() */
+ return (-1); /* Errno is set already */
+
+ case 0: /* Child process */
+
+ status = umask(0); /* Get current umask */
+ status = umask(status | (0777 & ~dmode)); /* Set for mkdir */
+ execl("/bin/mkdir", "mkdir", dpath, (char *) 0);
+ _exit(-1); /* Can't exec /bin/mkdir */
+
+ default: /* Parent process */
+ while (cpid != wait(&status)) {
+ /* Wait for child to finish */
+ }
+ }
+puts("a fork1");
+
+ if (TERM_SIGNAL(status) != 0 || TERM_VALUE(status) != 0) {
+ errno = EIO; /* We don't know why, but */
+ return (-1); /* /bin/mkdir failed */
+ }
+ return (0);
+}
+
+
+/* rmdir - remove a directory
+ *
+ * DESCRIPTION
+ *
+ * Rmdir will remove the directory specified by "dpath". It is
+ * consistent with the BSD and POSIX rmdir functions.
+ *
+ * PARAMETERS
+ *
+ * dpath - name of directory to remove
+ *
+ * RETURNS
+ *
+ * Returns 0 if the directory was successfully deleted, otherwise a
+ * non-zero return value will be passed back to the calling function
+ * and the value of errno should reflect the error.
+ */
+
+#ifdef __STDC__
+
+int rmdir(char *dpath)
+
+#else
+
+int rmdir(dpath)
+char *dpath;
+
+#endif
+{
+ int cpid, status;
+ Stat statbuf;
+ extern int errno;
+
+ /* check to see if it exists */
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int rmdir() in port.c\n");
+#endif
+ if (STAT(dpath, &statbuf) == -1) {
+ return (-1);
+ }
+puts("b fork");
+ switch (cpid = fork()) {
+
+ case -1: /* Error in fork() */
+ return (-1); /* Errno is set already */
+
+ case 0: /* Child process */
+ execl("/bin/rmdir", "rmdir", dpath, (char *) 0);
+ _exit(-1); /* Can't exec /bin/rmdir */
+
+ default: /* Parent process */
+ while (cpid != wait(&status)) {
+ /* Wait for child to finish */
+ }
+ }
+puts("a fork");
+
+ if (TERM_SIGNAL(status) != 0 || TERM_VALUE(status) != 0) {
+ errno = EIO; /* We don't know why, but */
+ return (-1); /* /bin/rmdir failed */
+ }
+ return (0);
+}
+
+#endif /* MASSCOMP, BSD */
diff --git a/private/posix/programs/pax/port.h b/private/posix/programs/pax/port.h
new file mode 100644
index 000000000..a292576fe
--- /dev/null
+++ b/private/posix/programs/pax/port.h
@@ -0,0 +1,93 @@
+/* $Source: /u/mark/src/pax/RCS/port.h,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * port.h - defnitions for portability library
+ *
+ * DESCRIPTION
+ *
+ * Header for maintaing portablilty across operating system and
+ * version boundries. For the most part, this file contains
+ * definitions which map functions which have the same functionality
+ * but different names on different systems, to have the same name.
+ *
+ * AUTHORS
+ *
+ * Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
+ * John Gilmore (gnu@hoptoad)
+ *
+ * Sponsored by The USENIX Association for public distribution.
+ *
+ * Copyright (c) 1989 Mark H. Colburn.
+ * 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 Mark H. Colburn and sponsored by The USENIX Association.
+ *
+ * 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 _PAX_PORT_H
+#define _PAX_PORT_H
+
+/*
+ * Everybody does wait() differently. There seem to be no definitions for
+ * this in V7 (e.g. you are supposed to shift and mask things out using
+ * constant shifts and masks.) In order to provide the functionality, here
+ * are some non standard but portable macros. Don't change to a "union wait"
+ * based approach -- the ordering of the elements of the struct depends on the
+ * byte-sex of the machine.
+ */
+
+#define TERM_SIGNAL(status) ((status) & 0x7F)
+#define TERM_COREDUMP(status) (((status) & 0x80) != 0)
+#define TERM_VALUE(status) ((status) >> 8)
+
+/*
+ * String library emulation definitions for the different variants of UNIX
+ */
+
+#if defined(USG)
+
+# include <string.h>
+# include <memory.h>
+
+#else /* USG */
+
+/*
+ * The following functions are defined here since func.h has no idea which
+ * of the functions will actually be used.
+ */
+# ifdef __STDC__
+extern char *rindex(char *, char);
+extern char *index(char *, char);
+extern char *bcopy(char *, char *, unsigned int);
+extern char *bzero(char *, unsigned int);
+extern char *strcat(char *, char *);
+extern char *strcpy(char *, char *);
+# else /* !__STDC__ */
+extern char *rindex();
+extern char *index();
+extern char *bcopy();
+extern char *bzero();
+extern char *strcat();
+extern char *strcpy();
+# endif /* __STDC__ */
+
+/*
+ * Map ANSI C compatible functions to V7 functions
+ */
+
+# define memcpy(a,b,n) bcopy((b),(a),(n))
+# define memset(a,b,n) bzero((a),(n))
+# define strrchr(s,c) rindex(s,c)
+# define strchr(s,c) index(s,c)
+
+#endif /* USG */
+#endif /* _PAX_PORT_H */
diff --git a/private/posix/programs/pax/regexp.c b/private/posix/programs/pax/regexp.c
new file mode 100644
index 000000000..49c00354b
--- /dev/null
+++ b/private/posix/programs/pax/regexp.c
@@ -0,0 +1,1511 @@
+/* $Source: /u/mark/src/pax/RCS/regexp.c,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * regexp.c - regular expression matching
+ *
+ * DESCRIPTION
+ *
+ * Underneath the reformatting and comment blocks which were added to
+ * make it consistent with the rest of the code, you will find a
+ * modified version of Henry Specer's regular expression library.
+ * Henry's functions were modified to provide the minimal regular
+ * expression matching, as required by P1003. Henry's code was
+ * copyrighted, and copy of the copyright message and restrictions
+ * are provided, verbatim, below:
+ *
+ * Copyright (c) 1986 by University of Toronto.
+ * Written by Henry Spencer. Not derived from licensed software.
+ *
+ * Permission is granted to anyone to use this software for any
+ * purpose on any computer system, and to redistribute it freely,
+ * subject to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of
+ * this software, no matter how awful, even if they arise
+ * from defects in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either
+ * by explicit claim or by omission.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * Beware that some of this code is subtly aware of the way operator
+ * precedence is structured in regular expressions. Serious changes in
+ * regular-expression syntax might require a total rethink.
+ *
+ * AUTHORS
+ *
+ * Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
+ * Henry Spencer, University of Torronto (henry@utzoo.edu)
+ *
+ * Sponsored by The USENIX Association for public distribution.
+ *
+ * $Log: regexp.c,v $
+ * Revision 1.2 89/02/12 10:05:39 mark
+ * 1.2 release fixes
+ *
+ * Revision 1.1 88/12/23 18:02:32 mark
+ * Initial revision
+ *
+ */
+
+/* Headers */
+
+#include "pax.h"
+
+#ifndef lint
+static char *Ident = "$Id: regexp.c,v 1.2 89/02/12 10:05:39 mark Exp $";
+#endif
+
+
+/*
+ * The "internal use only" fields in regexp.h are present to pass info from
+ * compile to execute that permits the execute phase to run lots faster on
+ * simple cases. They are:
+ *
+ * regstart char that must begin a match; '\0' if none obvious
+ * reganch is the match anchored (at beginning-of-line only)?
+ * regmust string (pointer into program) that match must include, or NULL
+ * regmlen length of regmust string
+ *
+ * Regstart and reganch permit very fast decisions on suitable starting points
+ * for a match, cutting down the work a lot. Regmust permits fast rejection
+ * of lines that cannot possibly match. The regmust tests are costly enough
+ * that regcomp() supplies a regmust only if the r.e. contains something
+ * potentially expensive (at present, the only such thing detected is * or +
+ * at the start of the r.e., which can involve a lot of backup). Regmlen is
+ * supplied because the test in regexec() needs it and regcomp() is computing
+ * it anyway.
+ */
+
+/*
+ * Structure for regexp "program". This is essentially a linear encoding
+ * of a nondeterministic finite-state machine (aka syntax charts or
+ * "railroad normal form" in parsing technology). Each node is an opcode
+ * plus a "nxt" pointer, possibly plus an operand. "Nxt" pointers of
+ * all nodes except BRANCH implement concatenation; a "nxt" pointer with
+ * a BRANCH on both ends of it is connecting two alternatives. (Here we
+ * have one of the subtle syntax dependencies: an individual BRANCH (as
+ * opposed to a collection of them) is never concatenated with anything
+ * because of operator precedence.) The operand of some types of node is
+ * a literal string; for others, it is a node leading into a sub-FSM. In
+ * particular, the operand of a BRANCH node is the first node of the branch.
+ * (NB this is *not* a tree structure: the tail of the branch connects
+ * to the thing following the set of BRANCHes.) The opcodes are:
+ */
+
+/* definition number opnd? meaning */
+#define END 0 /* no End of program. */
+#define BOL 1 /* no Match "" at beginning of line. */
+#define EOL 2 /* no Match "" at end of line. */
+#define ANY 3 /* no Match any one character. */
+#define ANYOF 4 /* str Match any character in this string. */
+#define ANYBUT 5 /* str Match any character not in this
+ * string. */
+#define BRANCH 6 /* node Match this alternative, or the
+ * nxt... */
+#define BACK 7 /* no Match "", "nxt" ptr points backward. */
+#define EXACTLY 8 /* str Match this string. */
+#define NOTHING 9 /* no Match empty string. */
+#define STAR 10 /* node Match this (simple) thing 0 or more
+ * times. */
+#define OPEN 20 /* no Mark this point in input as start of
+ * #n. */
+ /* OPEN+1 is number 1, etc. */
+#define CLOSE 30 /* no Analogous to OPEN. */
+
+/*
+ * Opcode notes:
+ *
+ * BRANCH The set of branches constituting a single choice are hooked
+ * together with their "nxt" pointers, since precedence prevents
+ * anything being concatenated to any individual branch. The
+ * "nxt" pointer of the last BRANCH in a choice points to the
+ * thing following the whole choice. This is also where the
+ * final "nxt" pointer of each individual branch points; each
+ * branch starts with the operand node of a BRANCH node.
+ *
+ * BACK Normal "nxt" pointers all implicitly point forward; BACK
+ * exists to make loop structures possible.
+ *
+ * STAR complex '*', are implemented as circular BRANCH structures
+ * using BACK. Simple cases (one character per match) are
+ * implemented with STAR for speed and to minimize recursive
+ * plunges.
+ *
+ * OPEN,CLOSE ...are numbered at compile time.
+ */
+
+/*
+ * A node is one char of opcode followed by two chars of "nxt" pointer.
+ * "Nxt" pointers are stored as two 8-bit pieces, high order first. The
+ * value is a positive offset from the opcode of the node containing it.
+ * An operand, if any, simply follows the node. (Note that much of the
+ * code generation knows about this implicit relationship.)
+ *
+ * Using two bytes for the "nxt" pointer is vast overkill for most things,
+ * but allows patterns to get big without disasters.
+ */
+#define OP(p) (*(p))
+#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377))
+#define OPERAND(p) ((p) + 3)
+
+/*
+ * Utility definitions.
+ */
+
+#define FAIL(m) { regerror(m); return(NULL); }
+#define ISMULT(c) ((c) == '*')
+#define META "^$.[()|*\\"
+#ifndef CHARBITS
+#define UCHARAT(p) ((int)*(unsigned char *)(p))
+#else
+#define UCHARAT(p) ((int)*(p)&CHARBITS)
+#endif
+
+/*
+ * Flags to be passed up and down.
+ */
+#define HASWIDTH 01 /* Known never to match null string. */
+#define SIMPLE 02 /* Simple enough to be STAR operand. */
+#define SPSTART 04 /* Starts with * */
+#define WORST 0 /* Worst case. */
+
+/*
+ * Global work variables for regcomp().
+ */
+static char *regparse; /* Input-scan pointer. */
+static int regnpar; /* () count. */
+static char regdummy;
+static char *regcode; /* Code-emit pointer; &regdummy = don't. */
+static long regsize; /* Code size. */
+
+/*
+ * Forward declarations for regcomp()'s friends.
+ */
+#ifndef STATIC
+#define STATIC static
+#endif
+#ifdef __STDC__ /* Xn */
+ /* Xn */
+STATIC char *reg(int, int *); /* Xn */
+STATIC char *regbranch(int *); /* Xn */
+STATIC char *regpiece(int *); /* Xn */
+STATIC char *regatom(int *); /* Xn */
+STATIC char *regnode(char); /* Xn */
+STATIC char *regnext(register char *); /* Xn */
+STATIC void regc(char); /* Xn */
+STATIC void reginsert(char, char *); /* Xn */
+STATIC void regtail(char *, char *); /* Xn */
+STATIC void regoptail(char *, char *); /* Xn */
+#ifdef STRCSPN
+STATIC size_t strcspn(const char *, const char *); /* Xn */
+#endif
+ /* Xn */
+#else /* Xn */
+ /* Xn */
+STATIC char *reg();
+STATIC char *regbranch();
+STATIC char *regpiece();
+STATIC char *regatom();
+STATIC char *regnode();
+STATIC char *regnext();
+STATIC void regc();
+STATIC void reginsert();
+STATIC void regtail();
+STATIC void regoptail();
+#ifdef STRCSPN
+STATIC uint strcspn(); /* Xn */
+#endif
+ /* Xn */
+#endif /* Xn */
+
+/*
+ - regcomp - compile a regular expression into internal code
+ *
+ * We can't allocate space until we know how big the compiled form will be,
+ * but we can't compile it (and thus know how big it is) until we've got a
+ * place to put the code. So we cheat: we compile it twice, once with code
+ * generation turned off and size counting turned on, and once "for real".
+ * This also means that we don't allocate space until we are sure that the
+ * thing really will compile successfully, and we never have to move the
+ * code and thus invalidate pointers into it. (Note that it has to be in
+ * one piece because free() must be able to free it all.)
+ *
+ * Beware that the optimization-preparation code in here knows about some
+ * of the structure of the compiled regexp.
+ */
+#ifdef __STDC__ /* Xn */
+ /* Xn */
+regexp *regcomp(char *exp) /* Xn */
+ /* Xn */
+#else
+ /* Xn */
+regexp *regcomp(exp)
+char *exp;
+ /* Xn */
+#endif /* Xn */
+{
+ register regexp *r;
+ register char *scan;
+ register char *longest;
+ register int len;
+ int flags;
+#ifndef _POSIX_SOURCE /* Xn */
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: regexp *regcomp() in regexp.c\n");
+#endif
+ extern char *malloc();
+#endif /* Xn */
+
+ if (exp == (char *)NULL)
+ FAIL("NULL argument");
+
+ /* First pass: determine size, legality. */
+ regparse = exp;
+ regnpar = 1;
+ regsize = 0L;
+ regcode = &regdummy;
+ regc(MAGIC);
+ if (reg(0, &flags) == (char *)NULL)
+ return ((regexp *)NULL);
+
+ /* Small enough for pointer-storage convention? */
+ if (regsize >= 32767L) /* Probably could be 65535L. */
+ FAIL("regexp too big");
+
+ /* Allocate space. */
+ r = (regexp *) malloc(sizeof(regexp) + (unsigned) regsize);
+ if (r == (regexp *) NULL)
+ FAIL("out of space");
+
+ /* Second pass: emit code. */
+ regparse = exp;
+ regnpar = 1;
+ regcode = r->program;
+ regc(MAGIC);
+ if (reg(0, &flags) == NULL)
+ return ((regexp *) NULL);
+
+ /* Dig out information for optimizations. */
+ r->regstart = '\0'; /* Worst-case defaults. */
+ r->reganch = 0;
+ r->regmust = NULL;
+ r->regmlen = 0;
+ scan = r->program + 1; /* First BRANCH. */
+ if (OP(regnext(scan)) == END) { /* Only one top-level choice. */
+ scan = OPERAND(scan);
+
+ /* Starting-point info. */
+ if (OP(scan) == EXACTLY)
+ r->regstart = *OPERAND(scan);
+ else if (OP(scan) == BOL)
+ r->reganch++;
+
+ /*
+ * If there's something expensive in the r.e., find the longest
+ * literal string that must appear and make it the regmust. Resolve
+ * ties in favor of later strings, since the regstart check works
+ * with the beginning of the r.e. and avoiding duplication
+ * strengthens checking. Not a strong reason, but sufficient in the
+ * absence of others.
+ */
+ if (flags & SPSTART) {
+ longest = NULL;
+ len = 0;
+ for (; scan != NULL; scan = regnext(scan))
+ if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) {
+ longest = OPERAND(scan);
+ len = strlen(OPERAND(scan));
+ }
+ r->regmust = longest;
+ r->regmlen = len;
+ }
+ }
+ return (r);
+}
+
+/*
+ - reg - regular expression, i.e. main body or parenthesized thing
+ *
+ * Caller must absorb opening parenthesis.
+ *
+ * Combining parenthesis handling with the base level of regular expression
+ * is a trifle forced, but the need to tie the tails of the branches to what
+ * follows makes it hard to avoid.
+ */
+#ifdef __STDC__ /* Xn */
+ /* Xn */
+static char *reg(int paren, int *flagp) /* Xn */
+ /* Xn */
+#else /* Xn */
+ /* Xn */
+static char *reg(paren, flagp)
+int paren; /* Parenthesized? */
+int *flagp;
+ /* Xn */
+#endif /* Xn */
+{
+ register char *ret;
+ register char *br;
+ register char *ender;
+ register int parno;
+ int flags;
+
+ *flagp = HASWIDTH; /* Tentatively. */
+
+ /* Make an OPEN node, if parenthesized. */
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static char *reg() in regexp.c\n");
+#endif
+ if (paren) {
+ if (regnpar >= NSUBEXP)
+ FAIL("too many ()");
+ parno = regnpar;
+ regnpar++;
+ ret = regnode(OPEN + parno);
+ } else
+ ret = (char *)NULL;
+
+ /* Pick up the branches, linking them together. */
+ br = regbranch(&flags);
+ if (br == (char *)NULL)
+ return ((char *)NULL);
+ if (ret != (char *)NULL)
+ regtail(ret, br); /* OPEN -> first. */
+ else
+ ret = br;
+ if (!(flags & HASWIDTH))
+ *flagp &= ~HASWIDTH;
+ *flagp |= flags & SPSTART;
+ while (*regparse == '|') {
+ regparse++;
+ br = regbranch(&flags);
+ if (br == (char *)NULL)
+ return ((char *)NULL);
+ regtail(ret, br); /* BRANCH -> BRANCH. */
+ if (!(flags & HASWIDTH))
+ *flagp &= ~HASWIDTH;
+ *flagp |= flags & SPSTART;
+ }
+
+ /* Make a closing node, and hook it on the end. */
+ ender = regnode((paren) ? CLOSE + parno : END);
+ regtail(ret, ender);
+
+ /* Hook the tails of the branches to the closing node. */
+ for (br = ret; br != (char *)NULL; br = regnext(br))
+ regoptail(br, ender);
+
+ /* Check for proper termination. */
+ if (paren && *regparse++ != ')') {
+ FAIL("unmatched ()");
+ } else if (!paren && *regparse != '\0') {
+ if (*regparse == ')') {
+ FAIL("unmatched ()");
+ } else
+ FAIL("junk on end");/* "Can't happen". */
+ /* NOTREACHED */
+ }
+ return (ret);
+}
+
+/*
+ - regbranch - one alternative of an | operator
+ *
+ * Implements the concatenation operator.
+ */
+#ifdef __STDC__ /* Xn */
+ /* Xn */
+static char *regbranch(int *flagp) /* Xn */
+ /* Xn */
+#else /* Xn */
+ /* Xn */
+static char *regbranch(flagp) /* Xn */
+int *flagp;
+ /* Xn */
+#endif /* Xn */
+{
+ register char *ret;
+ register char *chain;
+ register char *latest;
+ int flags;
+
+ *flagp = WORST; /* Tentatively. */
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static char *regbranch() in regexp.c\n");
+#endif
+ ret = regnode(BRANCH);
+ chain = (char *)NULL;
+ while (*regparse != '\0' && *regparse != '|' && *regparse != ')') {
+ latest = regpiece(&flags);
+ if (latest == (char *)NULL)
+ return ((char *)NULL);
+ *flagp |= flags & HASWIDTH;
+ if (chain == (char *)NULL) /* First piece. */
+ *flagp |= flags & SPSTART;
+ else
+ regtail(chain, latest);
+ chain = latest;
+ }
+ if (chain == (char *)NULL) /* Loop ran zero times. */
+ regnode(NOTHING);
+
+ return (ret);
+}
+
+/*
+ - regpiece - something followed by possible [*]
+ *
+ * Note that the branching code sequence used for * is somewhat optimized:
+ * they use the same NOTHING node as both the endmarker for their branch
+ * list and the body of the last branch. It might seem that this node could
+ * be dispensed with entirely, but the endmarker role is not redundant.
+ */
+#ifdef __STDC__ /* Xn */
+ /* Xn */
+static char *regpiece(int *flagp) /* Xn */
+ /* Xn */
+#else /* Xn */
+ /* Xn */
+static char *regpiece(flagp)
+int *flagp;
+ /* Xn */
+#endif /* Xn */
+{
+ register char *ret;
+ register char op;
+#if 0 /* Xn */
+ register char *nxt;
+#endif /* Xn */
+ int flags;
+
+ ret = regatom(&flags);
+ if (ret == (char *)NULL)
+ return ((char *)NULL);
+
+ op = *regparse;
+ if (!ISMULT(op)) {
+ *flagp = flags;
+ return (ret);
+ }
+ if (!(flags & HASWIDTH))
+ FAIL("* operand could be empty");
+ *flagp = (WORST | SPSTART);
+
+ if (op == '*' && (flags & SIMPLE))
+ reginsert(STAR, ret);
+ else if (op == '*') {
+ /* Emit x* as (x&|), where & means "self". */
+ reginsert(BRANCH, ret); /* Either x */
+ regoptail(ret, regnode(BACK)); /* and loop */
+ regoptail(ret, ret); /* back */
+ regtail(ret, regnode(BRANCH)); /* or */
+ regtail(ret, regnode(NOTHING)); /* null. */
+ }
+ regparse++;
+ if (ISMULT(*regparse))
+ FAIL("nested *");
+
+ return (ret);
+}
+
+/*
+ - regatom - the lowest level
+ *
+ * Optimization: gobbles an entire sequence of ordinary characters so that
+ * it can turn them into a single node, which is smaller to store and
+ * faster to run. Backslashed characters are exceptions, each becoming a
+ * separate node; the code is simpler that way and it's not worth fixing.
+ */
+#ifdef __STDC__ /* Xn */
+ /* Xn */
+static char *regatom(int *flagp) /* Xn */
+ /* Xn */
+#else /* Xn */
+ /* Xn */
+static char *regatom(flagp)
+int *flagp;
+ /* Xn */
+#endif /* Xn */
+{
+ register char *ret;
+ int flags;
+
+ *flagp = WORST; /* Tentatively. */
+
+ switch (*regparse++) {
+ case '^':
+ ret = regnode(BOL);
+ break;
+ case '$':
+ ret = regnode(EOL);
+ break;
+ case '.':
+ ret = regnode(ANY);
+ *flagp |= HASWIDTH | SIMPLE;
+ break;
+ case '[':{
+ register int class;
+ register int classend;
+
+ if (*regparse == '^') { /* Complement of range. */
+ ret = regnode(ANYBUT);
+ regparse++;
+ } else
+ ret = regnode(ANYOF);
+ if (*regparse == ']' || *regparse == '-')
+ regc(*regparse++);
+ while (*regparse != '\0' && *regparse != ']') {
+ if (*regparse == '-') {
+ regparse++;
+ if (*regparse == ']' || *regparse == '\0')
+ regc('-');
+ else {
+ class = UCHARAT(regparse - 2) + 1;
+ classend = UCHARAT(regparse);
+ if (class > classend + 1)
+ FAIL("invalid [] range");
+ for (; class <= classend; class++)
+ regc(class);
+ regparse++;
+ }
+ } else
+ regc(*regparse++);
+ }
+ regc('\0');
+ if (*regparse != ']')
+ FAIL("unmatched []");
+ regparse++;
+ *flagp |= HASWIDTH | SIMPLE;
+ }
+ break;
+ case '(':
+ ret = reg(1, &flags);
+ if (ret == (char *)NULL)
+ return ((char *)NULL);
+ *flagp |= flags & (HASWIDTH | SPSTART);
+ break;
+ case '\0':
+ case '|':
+ case ')':
+ FAIL("internal urp"); /* Supposed to be caught earlier. */
+#if 0 /* Xn */
+ break;
+#endif /* Xn */
+ case '*':
+ FAIL("* follows nothing");
+#if 0 /* Xn */
+ break;
+#endif /* Xn */
+ case '\\':
+ if (*regparse == '\0')
+ FAIL("trailing \\");
+ ret = regnode(EXACTLY);
+ regc(*regparse++);
+ regc('\0');
+ *flagp |= HASWIDTH | SIMPLE;
+ break;
+ default:{
+ register uint len; /* Xn */
+ register char ender;
+
+ regparse--;
+ len = strcspn(regparse, META);
+#if 0 /* Xn */
+ if (len <= 0)
+ FAIL("internal disaster");
+#endif /* Xn */
+ ender = *(regparse + len);
+ if (len > 1 && ISMULT(ender))
+ len--; /* Back off clear of * operand. */
+ *flagp |= HASWIDTH;
+ if (len == 1)
+ *flagp |= SIMPLE;
+ ret = regnode(EXACTLY);
+ while (len > 0) {
+ regc(*regparse++);
+ len--;
+ }
+ regc('\0');
+ }
+ break;
+ }
+
+ return (ret);
+}
+
+/*
+ - regnode - emit a node
+ */
+#ifdef __STDC__ /* Xn */
+ /* Xn */
+static char *regnode(char op) /* Xn */
+ /* Xn */
+#else /* Xn */
+ /* Xn */
+static char *regnode(op)
+char op;
+ /* Xn */
+#endif /* Xn */
+{
+ register char *ret;
+ register char *ptr;
+
+ ret = regcode;
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static char *regnode() in regexp.c\n");
+#endif
+ if (ret == &regdummy) {
+ regsize += 3;
+ return (ret);
+ }
+ ptr = ret;
+ *ptr++ = op;
+ *ptr++ = '\0'; /* Null "nxt" pointer. */
+ *ptr++ = '\0';
+ regcode = ptr;
+
+ return (ret);
+}
+
+/*
+ - regc - emit (if appropriate) a byte of code
+ */
+#ifdef __STDC__ /* Xn */
+ /* Xn */
+static void regc(char b) /* Xn */
+ /* Xn */
+#else /* Xn */
+ /* Xn */
+static void regc(b)
+char b;
+ /* Xn */
+#endif /* Xn */
+{
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static void regc() in regexp.c\n");
+#endif
+ if (regcode != &regdummy)
+ *regcode++ = b;
+ else
+ regsize++;
+}
+
+/*
+ - reginsert - insert an operator in front of already-emitted operand
+ *
+ * Means relocating the operand.
+ */
+#ifdef __STDC__ /* Xn */
+ /* Xn */
+static void reginsert(char op, char *opnd) /* Xn */
+ /* Xn */
+#else /* Xn */
+ /* Xn */
+static void reginsert(op, opnd)
+char op;
+char *opnd;
+ /* Xn */
+#endif /* Xn */
+{
+ register char *src;
+ register char *dst;
+ register char *place;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static void reginsert() in regexp.c\n");
+#endif
+ if (regcode == &regdummy) {
+ regsize += 3;
+ return;
+ }
+ src = regcode;
+ regcode += 3;
+ dst = regcode;
+ while (src > opnd)
+ *--dst = *--src;
+
+ place = opnd; /* Op node, where operand used to be. */
+ *place++ = op;
+ *place++ = '\0';
+ *place++ = '\0';
+}
+
+/*
+ - regtail - set the next-pointer at the end of a node chain
+ */
+#ifdef __STDC__ /* Xn */
+ /* Xn */
+static void regtail(char *p, char *val) /* Xn */
+ /* Xn */
+#else /* Xn */
+ /* Xn */
+static void regtail(p, val)
+char *p;
+char *val;
+ /* Xn */
+#endif /* Xn */
+{
+ register char *scan;
+ register char *temp;
+ register int offset;
+
+ if (p == &regdummy)
+ return;
+
+ /* Find last node. */
+ scan = p;
+ for (;;) {
+ temp = regnext(scan);
+ if (temp == (char *)NULL)
+ break;
+ scan = temp;
+ }
+
+ if (OP(scan) == BACK)
+ offset = scan - val;
+ else
+ offset = val - scan;
+ *(scan + 1) = (offset >> 8) & 0377;
+ *(scan + 2) = offset & 0377;
+}
+
+/*
+ - regoptail - regtail on operand of first argument; nop if operandless
+ */
+#ifdef __STDC__ /* Xn */
+ /* Xn */
+static void regoptail(char *p, char *val) /* Xn */
+ /* Xn */
+#else /* Xn */
+ /* Xn */
+static void regoptail(p, val)
+char *p;
+char *val;
+ /* Xn */
+#endif /* Xn */
+{
+ /* "Operandless" and "op != BRANCH" are synonymous in practice. */
+ if (p == (char *)NULL || p == &regdummy || OP(p) != BRANCH)
+ return;
+ regtail(OPERAND(p), val);
+}
+
+/*
+ * regexec and friends
+ */
+
+/*
+ * Global work variables for regexec().
+ */
+static char *reginput; /* String-input pointer. */
+static char *regbol; /* Beginning of input, for ^ check. */
+static char **regstartp; /* Pointer to startp array. */
+static char **regendp; /* Ditto for endp. */
+
+/*
+ * Forwards.
+ */
+#ifdef __STDC__ /* Xn */
+ /* Xn */
+STATIC int regtry(regexp *, char *); /* Xn */
+STATIC int regmatch(char *); /* Xn */
+STATIC int regrepeat(char *); /* Xn */
+ /* Xn */
+#ifdef DEBUG /* Xn */
+int regnarrate = 0; /* Xn */
+void regdump(regexp *); /* Xn */
+STATIC char *regprop(char *); /* Xn */
+#endif /* Xn */
+ /* Xn */
+#else /* Xn */
+ /* Xn */
+STATIC int regtry();
+STATIC int regmatch();
+STATIC int regrepeat();
+
+#ifdef DEBUG
+int regnarrate = 0;
+void regdump();
+STATIC char *regprop();
+#endif
+ /* Xn */
+#endif /* Xn */
+
+/*
+ - regexec - match a regexp against a string
+ */
+#ifdef __STDC__ /* Xn */
+ /* Xn */
+int regexec(register regexp *prog, register char *string) /* Xn */
+ /* Xn */
+#else
+ /* Xn */
+int regexec(prog, string)
+register regexp *prog;
+register char *string;
+ /* Xn */
+#endif /* Xn */
+{
+ register char *s;
+
+ /* Be paranoid... */
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int regexec() in regexp.c\n");
+#endif
+ if (prog == (regexp *)NULL || string == (char *)NULL) {
+ regerror("NULL parameter");
+ return (0);
+ }
+ /* Check validity of program. */
+ if (UCHARAT(prog->program) != MAGIC) {
+ regerror("corrupted program");
+ return (0);
+ }
+ /* If there is a "must appear" string, look for it. */
+ if (prog->regmust != (char *)NULL) {
+ s = string;
+ while ((s = strchr(s, prog->regmust[0])) != (char *)NULL) {
+ if (strncmp(s, prog->regmust, prog->regmlen) == 0)
+ break; /* Found it. */
+ s++;
+ }
+ if (s == (char *)NULL) /* Not present. */
+ return (0);
+ }
+ /* Mark beginning of line for ^ . */
+ regbol = string;
+
+ /* Simplest case: anchored match need be tried only once. */
+ if (prog->reganch)
+ return (regtry(prog, string));
+
+ /* Messy cases: unanchored match. */
+ s = string;
+ if (prog->regstart != '\0')
+ /* We know what char it must start with. */
+ while ((s = strchr(s, prog->regstart)) != (char *)NULL) {
+ if (regtry(prog, s))
+ return (1);
+ s++;
+ }
+ else
+ /* We don't -- general case. */
+ do {
+ if (regtry(prog, s))
+ return (1);
+ } while (*s++ != '\0');
+
+ /* Failure. */
+ return (0);
+}
+
+/*
+ - regtry - try match at specific point
+ */
+#ifdef __STDC__
+
+static int regtry(regexp *prog, char *string)
+
+#else
+
+static int regtry(prog, string)
+regexp *prog;
+char *string;
+
+#endif
+{
+ register int i;
+ register char **sp;
+ register char **ep;
+
+ reginput = string;
+ regstartp = prog->startp;
+ regendp = prog->endp;
+
+ sp = prog->startp;
+ ep = prog->endp;
+ for (i = NSUBEXP; i > 0; i--) {
+ *sp++ = (char *)NULL;
+ *ep++ = (char *)NULL;
+ }
+ if (regmatch(prog->program + 1)) {
+ prog->startp[0] = string;
+ prog->endp[0] = reginput;
+ return (1);
+ } else
+ return (0);
+}
+
+/*
+ - regmatch - main matching routine
+ *
+ * Conceptually the strategy is simple: check to see whether the current
+ * node matches, call self recursively to see whether the rest matches,
+ * and then act accordingly. In practice we make some effort to avoid
+ * recursion, in particular by going through "ordinary" nodes (that don't
+ * need to know whether the rest of the match failed) by a loop instead of
+ * by recursion.
+ */
+#ifdef __STDC__
+
+static int regmatch(char *prog)
+
+#else
+
+static int regmatch(prog)
+char *prog;
+
+#endif
+{
+ register char *scan; /* Current node. */
+ char *nxt; /* nxt node. */
+
+ scan = prog;
+#ifdef DEBUG
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static int regmatch() in regexp.c\n");
+#endif
+ if (scan != (char *)NULL && regnarrate)
+ fprintf(stderr, "%s(\n", regprop(scan));
+#endif
+ while (scan != (char *)NULL) {
+#ifdef DEBUG
+ if (regnarrate)
+ fprintf(stderr, "%s...\n", regprop(scan));
+#endif
+ nxt = regnext(scan);
+
+ switch (OP(scan)) {
+ case BOL:
+ if (reginput != regbol)
+ return (0);
+ break;
+ case EOL:
+ if (*reginput != '\0')
+ return (0);
+ break;
+ case ANY:
+ if (*reginput == '\0')
+ return (0);
+ reginput++;
+ break;
+ case EXACTLY:{
+ register int len;
+ register char *opnd;
+
+ opnd = OPERAND(scan);
+ /* Inline the first character, for speed. */
+ if (*opnd != *reginput)
+ return (0);
+ len = strlen(opnd);
+ if (len > 1 && strncmp(opnd, reginput, len) != 0)
+ return (0);
+ reginput += len;
+ }
+ break;
+ case ANYOF:
+ if (*reginput == '\0' ||
+ strchr(OPERAND(scan), *reginput) == (char *)NULL)
+ return (0);
+ reginput++;
+ break;
+ case ANYBUT:
+ if (*reginput == '\0' ||
+ strchr(OPERAND(scan), *reginput) != (char *)NULL)
+ return (0);
+ reginput++;
+ break;
+ case NOTHING:
+ break;
+ case BACK:
+ break;
+ case OPEN + 1:
+ case OPEN + 2:
+ case OPEN + 3:
+ case OPEN + 4:
+ case OPEN + 5:
+ case OPEN + 6:
+ case OPEN + 7:
+ case OPEN + 8:
+ case OPEN + 9:{
+ register int no;
+ register char *save;
+
+ no = OP(scan) - OPEN;
+ save = reginput;
+
+ if (regmatch(nxt)) {
+ /*
+ * Don't set startp if some later invocation of the same
+ * parentheses already has.
+ */
+ if (regstartp[no] == (char *)NULL)
+ regstartp[no] = save;
+ return (1);
+ } else
+ return (0);
+ }
+#if 0 /* Xn */
+ break;
+#endif /* Xn */
+ case CLOSE + 1:
+ case CLOSE + 2:
+ case CLOSE + 3:
+ case CLOSE + 4:
+ case CLOSE + 5:
+ case CLOSE + 6:
+ case CLOSE + 7:
+ case CLOSE + 8:
+ case CLOSE + 9:{
+ register int no;
+ register char *save;
+
+ no = OP(scan) - CLOSE;
+ save = reginput;
+
+ if (regmatch(nxt)) {
+ /*
+ * Don't set endp if some later invocation of the same
+ * parentheses already has.
+ */
+ if (regendp[no] == (char *)NULL)
+ regendp[no] = save;
+ return (1);
+ } else
+ return (0);
+ }
+#if 0 /* Xn */
+ break;
+#endif /* Xn */
+ case BRANCH:{
+ register char *save;
+
+ if (OP(nxt) != BRANCH) /* No choice. */
+ nxt = OPERAND(scan); /* Avoid recursion. */
+ else {
+ do {
+ save = reginput;
+ if (regmatch(OPERAND(scan)))
+ return (1);
+ reginput = save;
+ scan = regnext(scan);
+ } while (scan != (char *)NULL && OP(scan) == BRANCH);
+ return (0);
+ /* NOTREACHED */
+ }
+#if 0 /* Xn */
+ }
+ break;
+#else /* Xn */
+ break; /* Xn */
+ } /* Xn */
+#endif /* Xn */
+ case STAR:{
+ register char nextch;
+ register int no;
+ register char *save;
+ register int minimum;
+
+ /*
+ * Lookahead to avoid useless match attempts when we know
+ * what character comes next.
+ */
+ nextch = '\0';
+ if (OP(nxt) == EXACTLY)
+ nextch = *OPERAND(nxt);
+ minimum = (OP(scan) == STAR) ? 0 : 1;
+ save = reginput;
+ no = regrepeat(OPERAND(scan));
+ while (no >= minimum) {
+ /* If it could work, try it. */
+ if (nextch == '\0' || *reginput == nextch)
+ if (regmatch(nxt))
+ return (1);
+ /* Couldn't or didn't -- back up. */
+ no--;
+ reginput = save + no;
+ }
+ return (0);
+ }
+#if 0 /* Xn */
+ break;
+#endif /* Xn */
+ case END:
+ return (1); /* Success! */
+#if 0 /* Xn */
+ break;
+#endif /* Xn */
+ default:
+ regerror("memory corruption");
+ return (0);
+#if 0 /* Xn */
+ break;
+#endif /* Xn */
+ }
+
+ scan = nxt;
+ }
+
+ /*
+ * We get here only if there's trouble -- normally "case END" is the
+ * terminating point.
+ */
+ regerror("corrupted pointers");
+ return (0);
+}
+
+/*
+ - regrepeat - repeatedly match something simple, report how many
+ */
+#ifdef __STDC__
+
+static int regrepeat(char *p)
+
+#else
+
+static int regrepeat(p)
+char *p;
+
+#endif
+{
+ register int count = 0;
+ register char *scan;
+ register char *opnd;
+
+ scan = reginput;
+ opnd = OPERAND(p);
+ switch (OP(p)) {
+ case ANY:
+ count = strlen(scan);
+ scan += count;
+ break;
+ case EXACTLY:
+ while (*opnd == *scan) {
+ count++;
+ scan++;
+ }
+ break;
+ case ANYOF:
+ while (*scan != '\0' && strchr(opnd, *scan) != (char *)NULL) {
+ count++;
+ scan++;
+ }
+ break;
+ case ANYBUT:
+ while (*scan != '\0' && strchr(opnd, *scan) == (char *)NULL) {
+ count++;
+ scan++;
+ }
+ break;
+ default: /* Oh dear. Called inappropriately. */
+ regerror("internal foulup");
+ count = 0; /* Best compromise. */
+ break;
+ }
+ reginput = scan;
+
+ return (count);
+}
+
+
+/*
+ - regnext - dig the "nxt" pointer out of a node
+ */
+#ifdef __STDC__
+
+static char *regnext(register char *p)
+
+#else
+
+static char *regnext(p)
+register char *p;
+
+#endif
+{
+ register int offset;
+
+ if (p == &regdummy)
+ return ((char *)NULL);
+
+ offset = NEXT(p);
+ if (offset == 0)
+ return ((char *)NULL);
+
+ if (OP(p) == BACK)
+ return (p - offset);
+ else
+ return (p + offset);
+}
+
+#ifdef DEBUG
+
+#if 0 /* Xn */
+STATIC char *regprop();
+#endif /* Xn */
+
+/*
+ - regdump - dump a regexp onto stdout in vaguely comprehensible form
+ */
+#ifdef __STDC__
+
+void regdump(regexp *r)
+
+#else
+
+void regdump(r)
+regexp *r;
+
+#endif
+{
+ register char *s;
+ register char op = EXACTLY; /* Arbitrary non-END op. */
+ register char *nxt;
+#if 0 /* Xn */
+ extern char *strchr();
+#endif /* Xn */
+
+
+ s = r->program + 1;
+ while (op != END) { /* While that wasn't END last time... */
+ op = OP(s);
+ printf("%2d%s", s - r->program, regprop(s)); /* Where, what. */
+ nxt = regnext(s);
+ if (nxt == (char *)NULL) /* nxt ptr. */
+ printf("(0)");
+ else
+ printf("(%d)", (s - r->program) + (nxt - s));
+ s += 3;
+ if (op == ANYOF || op == ANYBUT || op == EXACTLY) {
+ /* Literal string, where present. */
+ while (*s != '\0') {
+ putchar(*s);
+ s++;
+ }
+ s++;
+ }
+ putchar('\n');
+ }
+
+ /* Header fields of interest. */
+ if (r->regstart != '\0')
+ printf("start `%c' ", r->regstart);
+ if (r->reganch)
+ printf("anchored ");
+ if (r->regmust != (char *)NULL)
+ printf("must have \"%s\"", r->regmust);
+ printf("\n");
+}
+
+/*
+ - regprop - printable representation of opcode
+ */
+#ifdef __STDC__
+
+static char *regprop(char *op)
+
+#else
+
+static char *regprop(op)
+char *op;
+
+#endif
+{
+ register char *p;
+ static char buf[50];
+
+ strcpy(buf, ":");
+
+ switch (OP(op)) {
+ case BOL:
+ p = "BOL";
+ break;
+ case EOL:
+ p = "EOL";
+ break;
+ case ANY:
+ p = "ANY";
+ break;
+ case ANYOF:
+ p = "ANYOF";
+ break;
+ case ANYBUT:
+ p = "ANYBUT";
+ break;
+ case BRANCH:
+ p = "BRANCH";
+ break;
+ case EXACTLY:
+ p = "EXACTLY";
+ break;
+ case NOTHING:
+ p = "NOTHING";
+ break;
+ case BACK:
+ p = "BACK";
+ break;
+ case END:
+ p = "END";
+ break;
+ case OPEN + 1:
+ case OPEN + 2:
+ case OPEN + 3:
+ case OPEN + 4:
+ case OPEN + 5:
+ case OPEN + 6:
+ case OPEN + 7:
+ case OPEN + 8:
+ case OPEN + 9:
+ sprintf(buf + strlen(buf), "OPEN%d", OP(op) - OPEN);
+ p = (char *)NULL;
+ break;
+ case CLOSE + 1:
+ case CLOSE + 2:
+ case CLOSE + 3:
+ case CLOSE + 4:
+ case CLOSE + 5:
+ case CLOSE + 6:
+ case CLOSE + 7:
+ case CLOSE + 8:
+ case CLOSE + 9:
+ sprintf(buf + strlen(buf), "CLOSE%d", OP(op) - CLOSE);
+ p = (char *)NULL;
+ break;
+ case STAR:
+ p = "STAR";
+ break;
+ default:
+ regerror("corrupted opcode");
+ break;
+ }
+ if (p != (char *)NULL)
+ strcat(buf, p);
+ return (buf);
+}
+#endif
+
+/*
+ * The following is provided for those people who do not have strcspn() in
+ * their C libraries. They should get off their butts and do something
+ * about it; at least one public-domain implementation of those (highly
+ * useful) string routines has been published on Usenet.
+ */
+#ifdef STRCSPN
+/*
+ * strcspn - find length of initial segment of s1 consisting entirely
+ * of characters not from s2
+ */
+
+#ifdef __STDC__
+
+static size_t strcspn(const char *s1, const char *s2) /* Xn */
+
+#else
+
+static uint strcspn(s1, s2) /* Xn */
+char *s1;
+char *s2;
+
+#endif
+{
+#ifdef __STDC__ /* Xn */
+ register const char *scan1; /* Xn */
+ register const char *scan2; /* Xn */
+ register size_t count; /* Xn */
+#else /* Xn */
+ register char *scan1;
+ register char *scan2;
+ register uint count; /* Xn */
+#endif /* Xn */
+
+ count = 0;
+ for (scan1 = s1; *scan1 != '\0'; scan1++) {
+ for (scan2 = s2; *scan2 != '\0';) /* ++ moved down. */
+ if (*scan1 == *scan2++)
+ return (count);
+ count++;
+ }
+ return (count);
+}
+#endif
+
+
+/*
+ - regsub - perform substitutions after a regexp match
+ */
+#ifdef __STDC__
+
+void regsub(regexp *prog, char *source, char *dest)
+
+#else
+
+void regsub(prog, source, dest)
+regexp *prog;
+char *source;
+char *dest;
+
+#endif
+{
+ register char *src;
+ register char *dst;
+ register char c;
+ register int no;
+ register int len;
+ extern char *strncpy();
+
+ if (prog == (regexp *)NULL ||
+ source == (char *)NULL || dest == (char *)NULL) {
+ regerror("NULL parm to regsub");
+ return;
+ }
+ if (UCHARAT(prog->program) != MAGIC) {
+ regerror("damaged regexp fed to regsub");
+ return;
+ }
+ src = source;
+ dst = dest;
+ while ((c = *src++) != '\0') {
+ if (c == '&')
+ no = 0;
+ else if (c == '\\' && '0' <= *src && *src <= '9')
+ no = *src++ - '0';
+ else
+ no = -1;
+
+ if (no < 0) { /* Ordinary character. */
+ if (c == '\\' && (*src == '\\' || *src == '&'))
+ c = *src++;
+ *dst++ = c;
+ } else if (prog->startp[no] != (char *)NULL &&
+ prog->endp[no] != (char *)NULL) {
+ len = prog->endp[no] - prog->startp[no];
+ strncpy(dst, prog->startp[no], len);
+ dst += len;
+ if (len != 0 && *(dst - 1) == '\0') { /* strncpy hit NUL. */
+ regerror("damaged match string");
+ return;
+ }
+ }
+ }
+ *dst++ = '\0';
+}
+
+
+#ifdef __STDC__
+
+void regerror(char *s)
+
+#else
+
+void regerror(s)
+char *s;
+
+#endif
+{
+ fprintf(stderr, "regexp(3): %s", s);
+ exit(1);
+}
diff --git a/private/posix/programs/pax/regexp.h b/private/posix/programs/pax/regexp.h
new file mode 100644
index 000000000..ae38080b8
--- /dev/null
+++ b/private/posix/programs/pax/regexp.h
@@ -0,0 +1,41 @@
+/*
+ * Definitions etc. for regexp(3) routines.
+ *
+ * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof],
+ * not the System V one.
+ */
+
+#ifndef _PAX_REGEXP_H
+#define _PAX_REGEXP_H
+
+#define NSUBEXP 10
+typedef struct regexp {
+ char *startp[NSUBEXP];
+ char *endp[NSUBEXP];
+ char regstart; /* Internal use only. */
+ char reganch; /* Internal use only. */
+ char *regmust; /* Internal use only. */
+ int regmlen; /* Internal use only. */
+ char program[1]; /* Unwarranted chumminess with compiler. */
+} regexp;
+
+
+/*
+ * The first byte of the regexp internal "program" is actually this magic
+ * number; the start node begins in the second byte.
+ */
+#define MAGIC 0234
+
+#ifdef __STDC__ /* Xn */
+extern regexp *regcomp(char *);
+extern int regexec(register regexp *, register char *);
+extern void regsub(regexp *, char *, char *);
+extern void regerror(char *);
+#else /* Xn */
+extern regexp *regcomp();
+extern int regexec();
+extern void regsub();
+extern void regerror();
+#endif /* Xn */
+
+#endif /* _PAX_REGEXP_H */
diff --git a/private/posix/programs/pax/replace.c b/private/posix/programs/pax/replace.c
new file mode 100644
index 000000000..e76cd542f
--- /dev/null
+++ b/private/posix/programs/pax/replace.c
@@ -0,0 +1,320 @@
+/* $Source: /u/mark/src/pax/RCS/replace.c,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * replace.c - regular expression pattern replacement functions
+ *
+ * DESCRIPTION
+ *
+ * These routines provide for regular expression file name replacement
+ * as required by pax.
+ *
+ * AUTHORS
+ *
+ * Mark H. Colburn, NAPS International
+ *
+ * Sponsored by The USENIX Association for public distribution.
+ *
+ * Copyright (c) 1989 Mark H. Colburn.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice is 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 Mark H. Colburn and sponsored by The
+ * USENIX Association.
+ *
+ * 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.
+ *
+ * $Log: replace.c,v $
+ * Revision 1.2 89/02/12 10:05:59 mark
+ * 1.2 release fixes
+ *
+ * Revision 1.1 88/12/23 18:02:36 mark
+ * Initial revision
+ *
+ */
+
+#ifndef lint
+static char *ident = "$Id: replace.c,v 1.2 89/02/12 10:05:59 mark Exp $";
+static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n";
+#endif /* not lint */
+
+/* Headers */
+
+#include "pax.h"
+
+
+/* add_replstr - add a replacement string to the replacement string list
+ *
+ * DESCRIPTION
+ *
+ * Add_replstr adds a replacement string to the replacement string
+ * list which is applied each time a file is about to be processed.
+ *
+ * PARAMETERS
+ *
+ * char *pattern - A regular expression which is to be parsed
+ */
+
+#ifdef __STDC__
+
+void add_replstr(char *pattern)
+
+#else
+
+void add_replstr(pattern)
+char *pattern;
+
+#endif
+{
+ char *p;
+ char sep;
+ Replstr *rptr;
+ int len;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: void add_replstr() in replace.c\n");
+#endif
+ if ((len = strlen(pattern)) < 4) {
+ warn("Replacement string not added",
+ "Malformed substitution syntax");
+ return;
+ }
+ if ((rptr = (Replstr *) malloc(sizeof(Replstr))) == (Replstr *)NULL) {
+ warn("Replacement string not added", "No space");
+ return;
+ }
+
+ /* First character is the delimeter... */
+ sep = *pattern;
+
+ /* Get trailing g and/or p */
+ p = pattern + len - 1;
+ while (*p != sep) {
+ if (*p == 'g') {
+ rptr->global = 1;
+ } else if (*p == 'p') {
+ rptr->print = 1;
+ } else {
+ warn(p, "Invalid RE modifier");
+ }
+ p--;
+ }
+
+ if (*p != sep) {
+ warn("Replacement string not added", "Bad delimeters");
+ free(rptr);
+ return;
+ }
+ /* strip off leading and trailing delimeter */
+ *p = '\0';
+ pattern++;
+
+ /* find the separating '/' in the pattern */
+ p = pattern;
+ while (*p) {
+ if (*p == sep) {
+ break;
+ }
+ if (*p == '\\' && *(p + 1) != '\0') {
+ p++;
+ }
+ p++;
+ }
+ if (*p != sep) {
+ warn("Replacement string not added", "Bad delimeters");
+ free(rptr);
+ return;
+ }
+ *p++ = '\0';
+
+ /*
+ * Now pattern points to 'old' and p points to 'new' and both are '\0'
+ * terminated
+ */
+ if ((rptr->comp = regcomp(pattern)) == (regexp *)NULL) {
+ warn("Replacement string not added", "Invalid RE");
+ free(rptr);
+ return;
+ }
+ rptr->replace = p;
+ rptr->next = (Replstr *)NULL;
+ if (rplhead == (Replstr *)NULL) {
+ rplhead = rptr;
+ rpltail = rptr;
+ } else {
+ rpltail->next = rptr;
+ rpltail = rptr;
+ }
+}
+
+
+
+/* rpl_name - possibly replace a name with a regular expression
+ *
+ * DESCRIPTION
+ *
+ * The string name is searched for in the list of regular expression
+ * substituions. If the string matches any of the regular expressions
+ * then the string is modified as specified by the user.
+ *
+ * PARAMETERS
+ *
+ * char *name - name to search for and possibly modify
+ */
+
+#ifdef __STDC__
+
+void rpl_name(char *name)
+
+#else
+
+void rpl_name(name)
+char *name;
+
+#endif
+{
+ int found = 0;
+ int ret;
+ Replstr *rptr;
+ char buff[PATH_MAX + 1];
+ char buff1[PATH_MAX + 1];
+ char buff2[PATH_MAX + 1];
+ char *p;
+ char *b;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: void rpl_name() in replace.c\n");
+#endif
+ strcpy(buff, name);
+ for (rptr = rplhead; !found && rptr != (Replstr *)NULL; rptr = rptr->next) {
+ do {
+ if ((ret = regexec(rptr->comp, buff)) != 0) {
+ p = buff;
+ b = buff1;
+ while (p < rptr->comp->startp[0]) {
+ *b++ = *p++;
+ }
+ p = rptr->replace;
+ while (*p) {
+ *b++ = *p++;
+ }
+ strcpy(b, rptr->comp->endp[0]);
+ found = 1;
+ regsub(rptr->comp, buff1, buff2);
+ strcpy(buff, buff2);
+ }
+ } while (ret && rptr->global);
+ if (found) {
+ if (rptr->print) {
+ fprintf(stderr, "%s >> %s\n", name, buff);
+ }
+ strcpy(name, buff);
+ }
+ }
+}
+
+
+/* get_disposition - get a file disposition
+ *
+ * DESCRIPTION
+ *
+ * Get a file disposition from the user. If the user enters 'y'
+ * the the file is processed, anything else and the file is ignored.
+ * If the user enters EOF, then the PAX exits with a non-zero return
+ * status.
+ *
+ * PARAMETERS
+ *
+ * char *mode - string signifying the action to be taken on file
+ * char *name - the name of the file
+ *
+ * RETURNS
+ *
+ * Returns 1 if the file should be processed, 0 if it should not.
+ */
+
+#ifdef __STDC__
+
+int get_disposition(char *mode, char *name)
+
+#else
+
+int get_disposition(mode, name)
+char *mode;
+char *name;
+
+#endif
+{
+ char ans[2];
+ char buf[PATH_MAX + 10];
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int get_disposition() in replace.c\n");
+#endif
+ if (f_disposition) {
+ sprintf(buf, "%s %s? ", mode, name);
+ if (nextask(buf, ans, sizeof(ans)) == -1 || ans[0] == 'q') {
+ exit(0);
+ }
+ if (strlen(ans) == 0 || ans[0] != 'y') {
+ return(1);
+ }
+ }
+ return(0);
+}
+
+
+/* get_newname - prompt the user for a new filename
+ *
+ * DESCRIPTION
+ *
+ * The user is prompted with the name of the file which is currently
+ * being processed. The user may choose to rename the file by
+ * entering the new file name after the prompt; the user may press
+ * carriage-return/newline, which will skip the file or the user may
+ * type an 'EOF' character, which will cause the program to stop.
+ *
+ * PARAMETERS
+ *
+ * char *name - filename, possibly modified by user
+ * int size - size of allowable new filename
+ *
+ * RETURNS
+ *
+ * Returns 0 if successfull, or -1 if an error occurred.
+ *
+ */
+
+#ifdef __STDC__
+
+int get_newname(char *name, int size)
+
+#else
+
+int get_newname(name, size)
+char *name;
+int size;
+
+#endif
+{
+ char buf[PATH_MAX + 10];
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int get_newname() in replace.c\n");
+#endif
+ if (f_interactive) {
+ sprintf(buf, "rename %s? ", name);
+ if (nextask(buf, name, size) == -1) {
+ exit(0);
+ }
+ if (strlen(name) == 0) {
+ return(1);
+ }
+ }
+ return(0);
+}
diff --git a/private/posix/programs/pax/sources b/private/posix/programs/pax/sources
new file mode 100644
index 000000000..8f17926df
--- /dev/null
+++ b/private/posix/programs/pax/sources
@@ -0,0 +1,43 @@
+WIMPYMASM=1
+386_STDCALL=0
+MAJORCOMP=posix
+MINORCOMP=client
+
+TARGETNAME=pax
+TARGETPATH=obj
+TARGETTYPE=PROGRAM
+
+INCLUDES=$(BASEDIR)\public\sdk\inc\posix;..\inc;..\inc\bsd;..\inc\df
+
+SOURCES= \
+ append.c \
+ buffer.c \
+ cpio.c \
+ create.c \
+ extract.c \
+ fileio.c \
+ getopt.c \
+ link.c \
+ list.c \
+ mem.c \
+ namelist.c \
+ names.c \
+ pass.c \
+ pathname.c \
+ pax.c \
+ port.c \
+ regexp.c \
+ replace.c \
+ tar.c \
+ ttyio.c \
+ warn.c \
+ wildmat.c \
+ pax.rc
+
+C_DEFINES=-DSTDC_HEADERS -D_POSIX_SOURCE -DDIRENT -DSTACK_DIRECTION=-1 -DDF_POSIX -Dmajor -D_POSIX_
+
+UMTYPE=posix
+
+UMLIBS=..\bsdlib\obj\*\bsdpsx.lib
+
+UMBASE=0xa00000
diff --git a/private/posix/programs/pax/stubs.c b/private/posix/programs/pax/stubs.c
new file mode 100644
index 000000000..e6b83a27e
--- /dev/null
+++ b/private/posix/programs/pax/stubs.c
@@ -0,0 +1,8 @@
+void seekdir() {
+puts("in seekdir");
+}
+
+int telldir() {
+puts("in telldir");
+ return 1;
+}
diff --git a/private/posix/programs/pax/tar.c b/private/posix/programs/pax/tar.c
new file mode 100644
index 000000000..7d091dff3
--- /dev/null
+++ b/private/posix/programs/pax/tar.c
@@ -0,0 +1,350 @@
+/* $Source: /u/mark/src/pax/RCS/tar.c,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * tar.c - tar specific functions for archive handling
+ *
+ * DESCRIPTION
+ *
+ * These routines provide a tar conforming interface to the pax
+ * program.
+ *
+ * AUTHOR
+ *
+ * Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
+ *
+ * Sponsored by The USENIX Association for public distribution.
+ *
+ * Copyright (c) 1989 Mark H. Colburn.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice is 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 Mark H. Colburn and sponsored by The
+ * USENIX Association.
+ *
+ * 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.
+ *
+ * $Log: tar.c,v $
+ * Revision 1.2 89/02/12 10:06:05 mark
+ * 1.2 release fixes
+ *
+ * Revision 1.1 88/12/23 18:02:38 mark
+ * Initial revision
+ *
+ */
+
+#ifndef lint
+static char *ident = "$Id: tar.c,v 1.2 89/02/12 10:06:05 mark Exp $";
+static char *copyright ="Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.";
+#endif /* not lint */
+
+/* Headers */
+
+#include "pax.h"
+
+
+/* Defines */
+
+#define DEF_BLOCKING 20 /* default blocking factor for extract */
+
+
+/* Function Prototypes */
+
+#ifdef __STDC__
+
+static int taropt(int , char **, char *);
+static void usage(void);
+
+#else /* !__STDC__ */
+
+static int taropt();
+static void usage();
+
+#endif /* __STDC__ */
+
+
+/* do_tar - main routine for tar.
+ *
+ * DESCRIPTION
+ *
+ * Provides a tar interface to the PAX program. All tar standard
+ * command line options are supported.
+ *
+ * PARAMETERS
+ *
+ * int argc - argument count (argc from main)
+ * char **argv - argument list (argv from main)
+ *
+ * RETURNS
+ *
+ * zero
+ */
+
+#ifdef __STDC__
+
+void do_tar(int argc, char **argv) /* Xn */
+
+#else
+
+void do_tar(argc, argv) /* Xn */
+int argc; /* argument count (argc from main) */
+char **argv; /* argument list (argv from main) */
+
+#endif
+{
+ int c; /* Option letter */
+
+ /* Set default option values */
+ names_from_stdin = 0;
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: void do_tar() in tar.c\n");
+#endif
+ ar_file = getenv("TAPE"); /* From environment, or */
+ if (ar_file == 0) {
+ ar_file = DEF_AR_FILE; /* From Makefile */
+ }
+
+ /*
+ * set up the flags to reflect the default pax inteface. Unfortunately
+ * the pax interface has several options which are completely opposite
+ * of the tar and/or cpio interfaces...
+ */
+ f_unconditional = 1;
+ f_mtime = 1;
+ f_dir_create = 1;
+ blocking = 0;
+ ar_interface = TAR;
+ ar_format = TAR;
+ msgfile=stderr;
+
+ /* Parse options */
+ while ((c = taropt(argc, argv, "b:cf:hlmortuvwx")) != EOF) {
+ switch (c) {
+ case 'b': /* specify blocking factor */
+ /*
+ * FIXME - we should use a conversion routine that does
+ * some kind of reasonable error checking, but...
+ */
+ blocking = atoi(optarg);
+ break;
+ case 'c': /* create a new archive */
+ f_create = 1;
+ break;
+ case 'f': /* specify input/output file */
+ ar_file = optarg;
+ break;
+ case 'h':
+ f_follow_links = 1; /* follow symbolic links */
+ break;
+ case 'l': /* report unresolved links */
+ f_linksleft = 1;
+ break;
+ case 'm': /* don't restore modification times */
+ f_modified = 1;
+ break;
+ case 'o': /* take on user's group rather than
+ * archives */
+ break;
+ case 'r': /* named files are appended to archive */
+ f_append = 1;
+ break;
+ case 't':
+ f_list = 1; /* list files in archive */
+ break;
+ case 'u': /* named files are added to archive */
+ f_newer = 1;
+ break;
+ case 'v': /* verbose mode */
+ f_verbose = 1;
+ break;
+ case 'w': /* user interactive mode */
+ f_disposition = 1;
+ break;
+ case 'x': /* named files are extracted from archive */
+ f_extract = 1;
+ break;
+ case '?':
+ usage();
+ exit(EX_ARGSBAD);
+ }
+ }
+
+ /* check command line argument sanity */
+ if (f_create + f_extract + f_list + f_append + f_newer != 1) {
+ (void) fprintf(stderr,
+ "%s: you must specify exactly one of the c, t, r, u or x options\n",
+ myname);
+ usage();
+ exit(EX_ARGSBAD);
+ }
+
+ /* set the blocking factor, if not set by the user */
+ if (blocking == 0) {
+#ifdef USG
+ if (f_extract || f_list) {
+ blocking = DEF_BLOCKING;
+ fprintf(stderr, "Tar: blocksize = %d\n", blocking);
+ } else {
+ blocking = 1;
+ }
+#else /* !USG */
+ blocking = 20;
+#endif /* USG */
+ }
+ blocksize = blocking * BLOCKSIZE;
+ buf_allocate((OFFSET) blocksize);
+
+ if (f_create) {
+ open_archive(AR_WRITE);
+ create_archive(); /* create the archive */
+ } else if (f_extract) {
+ open_archive(AR_READ);
+ read_archive(); /* extract files from archive */
+ } else if (f_list) {
+ open_archive(AR_READ);
+ read_archive(); /* read and list contents of archive */
+ } else if (f_append) {
+ open_archive(AR_APPEND);
+ append_archive(); /* append files to archive */
+ }
+
+ if (f_linksleft) {
+ linkleft(); /* report any unresolved links */
+ }
+#if 0 /* Xn */
+
+ return (0);
+#endif /* Xn */
+}
+
+
+/* taropt - tar specific getopt
+ *
+ * DESCRIPTION
+ *
+ * Plug-compatible replacement for getopt() for parsing tar-like
+ * arguments. If the first argument begins with "-", it uses getopt;
+ * otherwise, it uses the old rules used by tar, dump, and ps.
+ *
+ * PARAMETERS
+ *
+ * int argc - argument count (argc from main)
+ * char **argv - argument list (argv from main)
+ * char *optstring - sring which describes allowable options
+ *
+ * RETURNS
+ *
+ * Returns the next option character in the option string(s). If the
+ * option requires an argument and an argument was given, the argument
+ * is pointed to by "optarg". If no option character was found,
+ * returns an EOF.
+ *
+ */
+
+#ifdef __STDC__
+
+static int taropt(int argc, char **argv, char *optstring)
+
+#else
+
+static int taropt(argc, argv, optstring)
+int argc;
+char **argv;
+char *optstring;
+
+#endif
+{
+ extern char *optarg; /* Points to next arg */
+ extern int optind; /* Global argv index */
+ static char *key = NULL; /* Points to next keyletter */ /* Xn */
+ static char use_getopt = 0; /* !=0 if argv[1][0] was '-' */ /* Xn */
+ char c;
+ char *place;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static int taropt() in tar.c\n");
+#endif
+ optarg = (char *)NULL;
+
+ if (key == (char *)NULL) { /* First time */
+ if (argc < 2)
+ return EOF;
+ key = argv[1];
+ if (*key == '-')
+ use_getopt++;
+ else
+ optind = 2;
+ }
+ if (use_getopt) {
+#ifdef _POSIX2_SOURCE /* Xn */
+ return getopt(argc, (const char * const *) argv, optstring); /* Xn */
+#else /* Xn */
+ return getopt(argc, argv, optstring);
+#endif /* Xn */
+ }
+
+ c = *key++;
+ if (c == '\0') {
+ key--;
+ return EOF;
+ }
+ place = strchr(optstring, c);
+
+ if (place == (char *)NULL || c == ':') {
+ fprintf(stderr, "%s: unknown option %c\n", argv[0], c);
+ return ('?');
+ }
+ place++;
+ if (*place == ':') {
+ if (optind < argc) {
+ optarg = argv[optind];
+ optind++;
+ } else {
+ fprintf(stderr, "%s: %c argument missing\n",
+ argv[0], c);
+ return ('?');
+ }
+ }
+ return (c);
+}
+
+
+/* usage - print a helpful message and exit
+ *
+ * DESCRIPTION
+ *
+ * Usage prints out the usage message for the TAR interface and then
+ * exits with a non-zero termination status. This is used when a user
+ * has provided non-existant or incompatible command line arguments.
+ *
+ * RETURNS
+ *
+ * Returns an exit status of 1 to the parent process.
+ *
+ */
+
+#ifdef __STDC__
+
+static void usage(void)
+
+#else
+
+static void usage()
+
+#endif
+{
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static void usage() in tar.c\n");
+#endif
+ fprintf(stderr, "Usage: %s -c[bfvw] device block filename..\n", myname);
+ fprintf(stderr, " %s -r[bvw] device block [filename...]\n", myname);
+ fprintf(stderr, " %s -t[vf] device\n", myname);
+ fprintf(stderr, " %s -u[bvw] device block [filename...]\n", myname);
+ fprintf(stderr, " %s -x[flmovw] device [filename...]\n", myname);
+ exit(1);
+}
diff --git a/private/posix/programs/pax/ttyio.c b/private/posix/programs/pax/ttyio.c
new file mode 100644
index 000000000..c937ec704
--- /dev/null
+++ b/private/posix/programs/pax/ttyio.c
@@ -0,0 +1,289 @@
+/* $Source: /u/mark/src/pax/RCS/ttyio.c,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * ttyio.c - Terminal/Console I/O functions for all archive interfaces
+ *
+ * DESCRIPTION
+ *
+ * These routines provide a consistent, general purpose interface to
+ * the user via the users terminal, if it is available to the
+ * process.
+ *
+ * AUTHOR
+ *
+ * Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
+ *
+ * Sponsored by The USENIX Association for public distribution.
+ *
+ * Copyright (c) 1989 Mark H. Colburn.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice is 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 Mark H. Colburn and sponsored by The
+ * USENIX Association.
+ *
+ * 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.
+ *
+ * $Log: ttyio.c,v $
+ * Revision 1.2 89/02/12 10:06:11 mark
+ * 1.2 release fixes
+ *
+ * Revision 1.1 88/12/23 18:02:39 mark
+ * Initial revision
+ *
+ */
+
+#ifndef lint
+static char *ident = "$Id: ttyio.c,v 1.2 89/02/12 10:06:11 mark Exp $";
+static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n";
+#endif /* ! lint */
+
+
+/* Headers */
+
+#include "pax.h"
+
+
+/* open_tty - open the terminal for interactive queries
+ *
+ * DESCRIPTION
+ *
+ * Assumes that background processes ignore interrupts and that the
+ * open() or the isatty() will fail for processes which are not
+ * attached to terminals. Returns a file descriptor or -1 if
+ * unsuccessful.
+ *
+ * RETURNS
+ *
+ * Returns a file descriptor which can be used to read and write
+ * directly to the user's terminal, or -1 on failure.
+ *
+ * ERRORS
+ *
+ * If SIGINT cannot be ignored, or the open fails, or the newly opened
+ * terminal device is not a tty, then open_tty will return a -1 to the
+ * caller.
+ */
+
+#ifdef __STDC__
+
+int open_tty(void)
+
+#else
+
+int open_tty()
+
+#endif
+{
+ int fd; /* file descriptor for terminal */
+#ifdef __STDC__ /* Xn */
+ SIG_T (*intr)(int); /* used to restore interupts if signal fails */ /* Xn */
+#else /* Xn */
+ SIG_T (*intr)(); /* used to restore interupts if signal fails */
+#endif /* Xn */
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int open_tty() in ttyio.c\n");
+#endif
+ if ((intr = signal(SIGINT, SIG_IGN)) == SIG_IGN) {
+ return (-1);
+ }
+ signal(SIGINT, intr);
+ if ((fd = open(TTY, O_RDWR)) < 0) {
+ return (-1);
+ }
+ if (isatty(fd)) {
+ return (fd);
+ }
+ close(fd);
+ return (-1);
+}
+
+
+/* nextask - ask a question and get a response
+ *
+ * DESCRIPTION
+ *
+ * Give the user a prompt and wait for their response. The prompt,
+ * located in "msg" is printed, then the user is allowed to type
+ * a response to the message. The first "limit" characters of the
+ * user response is stored in "answer".
+ *
+ * Nextask ignores spaces and tabs.
+ *
+ * PARAMETERS
+ *
+ * char *msg - Message to display for user
+ * char *answer - Pointer to user's response to question
+ * int limit - Limit of length for user's response
+ *
+ * RETURNS
+ *
+ * Returns the number of characters in the user response to the
+ * calling function. If an EOF was encountered, a -1 is returned to
+ * the calling function. If an error occured which causes the read
+ * to return with a value of -1, then the function will return a
+ * non-zero return status to the calling process, and abort
+ * execution.
+ */
+
+#ifdef __STDC__
+
+int nextask(char *msg, char *answer, int limit)
+
+#else
+
+int nextask(msg, answer, limit)
+char *msg; /* message to display for user */
+char *answer; /* pointer to user's response to question */
+int limit; /* limit of length for user's response */
+
+#endif
+{
+ int idx; /* index into answer for character input */
+ int got; /* number of characters read */
+ char c; /* character read */
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int nextask() in ttyio.c\n");
+#endif
+ if (ttyf < 0) {
+ fatal("/dev/tty Unavailable");
+ }
+ write(ttyf, msg, (uint) strlen(msg));
+ idx = 0;
+/* while ((got = read(ttyf, &c, 1)) == 1) {
+ if (c == '\n') {
+ break;
+ } else if (c == ' ' || c == '\t') {
+ continue;
+ } else if (idx < limit - 1) {
+ answer[idx++] = c;
+ }
+ }
+05/04/90-JPB */
+ got = idx = read(ttyf, answer, limit); /* 05/04/90-JPB */
+ if (got == 0) { /* got an EOF */
+ return(-1);
+ }
+ if (got < 0) {
+ fatal(strerror(errno)); /* Xn */
+ }
+ answer[idx - 1] = '\0'; /* 05/04/90-JPB */
+ return(0);
+}
+
+
+/* lineget - get a line from a given stream
+ *
+ * DESCRIPTION
+ *
+ * Get a line of input for the stream named by "stream". The data on
+ * the stream is put into the buffer "buf".
+ *
+ * PARAMETERS
+ *
+ * FILE *stream - Stream to get input from
+ * char *buf - Buffer to put input into
+ *
+ * RETURNS
+ *
+ * Returns 0 if successful, -1 at EOF.
+ */
+
+#ifdef __STDC__
+
+int lineget(FILE *stream, char *buf)
+
+#else
+
+int lineget(stream, buf)
+FILE *stream; /* stream to get input from */
+char *buf; /* buffer to put input into */
+
+#endif
+{
+ int c;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int lineget() in ttyio.c\n");
+#endif
+ for (;;) {
+ if ((c = getc(stream)) == EOF) {
+ return (-1);
+ }
+ if (c == '\n') {
+ break;
+ }
+ *buf++ = c;
+ }
+ *buf = '\0';
+ return (0);
+}
+
+
+/* next - Advance to the next archive volume.
+ *
+ * DESCRIPTION
+ *
+ * Prompts the user to replace the backup medium with a new volume
+ * when the old one is full. There are some cases, such as when
+ * archiving to a file on a hard disk, that the message can be a
+ * little surprising. Assumes that background processes ignore
+ * interrupts and that the open() or the isatty() will fail for
+ * processes which are not attached to terminals. Returns a file
+ * descriptor or -1 if unsuccessful.
+ *
+ * PARAMETERS
+ *
+ * int mode - mode of archive (READ, WRITE, PASS)
+ */
+
+#ifdef __STDC__
+
+void next(int mode)
+
+#else
+
+void next(mode)
+int mode; /* mode of archive (READ, WRITE, PASS) */
+
+#endif
+{
+ static char go[] = "go"; /* keyword to proceed */ /* Xn */
+ static char quit[] = "quit"; /* keyword to abort */ /* Xn */
+ char msg[200]; /* buffer for message display */
+ char answer[20]; /* buffer for user's answer */
+ int ret;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: void next() in ttyio.c\n");
+#endif
+ close_archive();
+
+ sprintf(msg, "%s: Ready for volume %u\n%s: Type \"%s\" when ready to proceed (or \"%s\" to abort): ", /* Xn */
+ myname, arvolume + 1, myname, go, quit); /* Xn */
+#ifdef __STDC__ /* Xn */
+ if (!f_quiet) /* Xn */
+ (void) strcat(msg, "\a"); /* Xn */
+#else /* Xn */
+ if (!f_quiet) /* Xn */
+ (void) strcat(msg, "\07"); /* Xn */
+#endif /* Xn */
+ for (;;) {
+ ret = nextask(msg, answer, sizeof(answer));
+ if (ret == -1 || strcmp(answer, quit) == 0) { /* Xn */
+ fatal("Aborted");
+ }
+ if (strcmp(answer, go) == 0 && open_archive(mode) == 0) { /* Xn */
+ break;
+ }
+ }
+ warnarch("Continuing", (OFFSET) 0);
+}
diff --git a/private/posix/programs/pax/warn.c b/private/posix/programs/pax/warn.c
new file mode 100644
index 000000000..0a52c44fb
--- /dev/null
+++ b/private/posix/programs/pax/warn.c
@@ -0,0 +1,266 @@
+/* $Source: /u/mark/src/pax/RCS/warn.c,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * warn.c - miscellaneous user warning routines
+ *
+ * DESCRIPTION
+ *
+ * These routines provide the user with various forms of warning
+ * and informational messages.
+ *
+ * AUTHOR
+ *
+ * Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
+ *
+ * Sponsored by The USENIX Association for public distribution.
+ *
+ * Copyright (c) 1989 Mark H. Colburn.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice is 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 Mark H. Colburn and sponsored by The
+ * USENIX Association.
+ *
+ * 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.
+ *
+ * $Log: warn.c,v $
+ * Revision 1.2 89/02/12 10:06:15 mark
+ * 1.2 release fixes
+ *
+ * Revision 1.1 88/12/23 18:02:40 mark
+ * Initial revision
+ *
+ */
+
+#ifndef lint
+static char *ident = "$Id: warn.c,v 1.2 89/02/12 10:06:15 mark Exp $";
+static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n";
+#endif /* ! lint */
+
+
+/* Headers */
+
+#include "pax.h"
+
+
+/* Function Prototypes */
+
+#ifdef __STDC__
+
+static void prsize(FILE *, OFFSET);
+
+#else /* !__STDC__ */
+
+static void prsize();
+
+#endif /* __STDC__ */
+
+
+/* warnarch - print an archive-related warning message and offset
+ *
+ * DESCRIPTION
+ *
+ * Present the user with an error message and an archive offset at
+ * which the error occured. This can be useful for diagnosing or
+ * fixing damaged archives.
+ *
+ * PARAMETERS
+ *
+ * char *msg - A message string to be printed for the user.
+ * OFFSET adjust - An adjustment which is added to the current
+ * archive position to tell the user exactly where
+ * the error occurred.
+ */
+
+#ifdef __STDC__
+
+void warnarch(char *msg, OFFSET adjust)
+
+#else
+
+void warnarch(msg, adjust)
+char *msg;
+OFFSET adjust;
+
+#endif
+{
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: void warnarch() in warn.c\n");
+#endif
+ fprintf(stderr, "%s: [offset ", myname);
+ prsize(stderr, total - adjust);
+ fprintf(stderr, "]: %s\n", msg);
+}
+
+
+#ifdef STRERROR /* Xn */
+/* strerror - return pointer to appropriate system error message
+ *
+ * DESCRIPTION
+ *
+ * Get an error message string which is appropriate for the setting
+ * of the errno variable.
+ *
+ * RETURNS
+ *
+ * Returns a pointer to a string which has an appropriate error
+ * message for the present value of errno. The error message
+ * strings are taken from sys_errlist[] where appropriate. If an
+ * appropriate message is not available in sys_errlist, then a
+ * pointer to the string "Unknown error (errno <errvalue>)" is
+ * returned instead.
+ */
+
+#ifdef __STDC__
+
+char *strerror(int errno) /* Xn */
+
+#else
+
+char *strerror(errno) /* Xn */
+int errno; /* Xn */
+
+#endif
+{
+ static char msg[40]; /* used for "Unknown error" messages */
+
+#ifndef DF_POSIX //DF_MSS
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: char *strerror() in warn.c\n");
+#endif
+ if (errno > 0 && errno < sys_nerr) {
+ return (sys_errlist[errno]);
+ }
+#endif
+ sprintf(msg, "Unknown error (errno %d)", errno);
+ return (msg);
+}
+#endif /* Xn */
+
+
+/* prsize - print a file offset on a file stream
+ *
+ * DESCRIPTION
+ *
+ * Prints a file offset to a specific file stream. The file offset is
+ * of the form "%dm+%dk+%d", where the number preceeding the "m" and
+ * the "k" stand for the number of Megabytes and the number of
+ * Kilobytes, respectivley, which have been processed so far.
+ *
+ * PARAMETERS
+ *
+ * FILE *stream - Stream which is to be used for output
+ * OFFSET size - Current archive position to be printed on the output
+ * stream in the form: "%dm+%dk+%d".
+ *
+ */
+
+#ifdef __STDC__
+
+static void prsize(FILE *stream, OFFSET size)
+
+#else
+
+static void prsize(stream, size)
+FILE *stream; /* stream which is used for output */
+OFFSET size; /* current archive position to be printed */
+
+#endif
+
+{
+ OFFSET n;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static void prsize() in warn.c\n");
+#endif
+ if (n = (size / (1024L * 1024L))) {
+ fprintf(stream, "%ldm+", n);
+ size -= n * 1024L * 1024L;
+ }
+ if (n = (size / 1024L)) {
+ fprintf(stream, "%ldk+", n);
+ size -= n * 1024L;
+ }
+ fprintf(stream, "%ld", size);
+}
+
+
+/* fatal - print fatal message and exit
+ *
+ * DESCRIPTION
+ *
+ * Fatal prints the program's name along with an error message, then
+ * exits the program with a non-zero return code.
+ *
+ * PARAMETERS
+ *
+ * char *why - description of reason for termination
+ *
+ * RETURNS
+ *
+ * Returns an exit code of 1 to the parent process.
+ */
+
+#ifdef __STDC__
+
+void fatal(char *why)
+
+#else
+
+void fatal(why)
+char *why; /* description of reason for termination */
+
+#endif
+{
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: void fatal() in warn.c\n");
+#endif
+#ifdef _POSIX_SOURCE
+ perror(why);
+#endif
+ fprintf(stderr, "%s: %s\n", myname, why);
+ exit(1);
+}
+
+
+
+/* warn - print a warning message
+ *
+ * DESCRIPTION
+ *
+ * Print an error message listing the program name, the actual error
+ * which occurred and an informational message as to why the error
+ * occurred on the standard error device. The standard error is
+ * flushed after the error is printed to assure that the user gets
+ * the message in a timely fasion.
+ *
+ * PARAMETERS
+ *
+ * char *what - Pointer to string describing what failed.
+ * char *why - Pointer to string describing why did it failed.
+ */
+
+#ifdef __STDC__
+
+void warn(char *what, char *why)
+
+#else
+
+void warn(what, why)
+char *what; /* message as to what the error was */
+char *why; /* explanation why the error occurred */
+
+#endif
+{
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: void warn() in warn.c\n");
+#endif
+ fprintf(stderr, "%s: %s : %s\n", myname, what, why);
+ fflush(stderr);
+}
diff --git a/private/posix/programs/pax/wildmat.c b/private/posix/programs/pax/wildmat.c
new file mode 100644
index 000000000..b354d9b95
--- /dev/null
+++ b/private/posix/programs/pax/wildmat.c
@@ -0,0 +1,203 @@
+/* $Source: /u/mark/src/pax/RCS/wildmat.c,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * wildmat.c - simple regular expression pattern matching routines
+ *
+ * DESCRIPTION
+ *
+ * These routines provide simple UNIX style regular expression matching.
+ * They were originally written by Rich Salz, the comp.sources.unix
+ * moderator for inclusion in some of his software. These routines
+ * were released into the public domain and used by John Gilmore in
+ * USTAR.
+ *
+ * AUTHORS
+ *
+ * Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
+ * John Gilmore (gnu@hoptoad)
+ * Rich Salz (rs@uunet.uu.net)
+ *
+ *
+ * Sponsored by The USENIX Association for public distribution.
+ *
+ * Copyright (c) 1989 Mark H. Colburn.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice is 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 Mark H. Colburn and sponsored by The
+ * USENIX Association.
+ *
+ * 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.
+ *
+ * $Log: wildmat.c,v $
+ * Revision 1.2 89/02/12 10:06:20 mark
+ * 1.2 release fixes
+ *
+ * Revision 1.1 88/12/23 18:02:41 mark
+ * Initial revision
+ *
+ */
+
+#ifndef lint
+static char *ident = "$Id: wildmat.c,v 1.2 89/02/12 10:06:20 mark Exp $";
+static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n";
+#endif /* ! lint */
+
+
+/* Headers */
+
+#include "pax.h"
+
+
+/* Function Prototypes */
+
+#ifdef __STDC__
+static int star(char *, char *);
+#else /* !__STDC__ */
+static int star();
+#endif /* __STDC__ */
+
+
+/*
+ * star - handle trailing * in a regular expression
+ *
+ * DESCRIPTION
+ *
+ * Star is used to match filename expansions containing a trailing
+ * asterisk ('*'). Star call wildmat() to determine if the substring
+ * passed to it is matches the regular expression.
+ *
+ * PARAMETERS
+ *
+ * char *source - The source string which is to be compared to the
+ * regular expression pattern.
+ * char *pattern - The regular expression which we are supposed to
+ * match to.
+ *
+ * RETURNS
+ *
+ * Returns non-zero if the entire source string is completely matched by
+ * the regular expression pattern, returns 0 otherwise. This is used to
+ * see if *'s in a pattern matched the entire source string.
+ *
+ */
+
+#ifdef __STDC__
+
+static int star(char *source, char *pattern)
+
+#else
+
+static int star(source, pattern)
+char *source; /* source operand */
+char *pattern; /* regular expression to match */
+
+#endif
+{
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static int star() in wildmat.c\n");
+#endif
+ while (!wildmat(pattern, source)) {
+ if (*++source == '\0') {
+ return (0);
+ }
+ }
+ return (1);
+}
+
+
+/*
+ * wildmat - match a regular expression
+ *
+ * DESCRIPTION
+ *
+ * Wildmat attempts to match the string pointed to by source to the
+ * regular expression pointed to by pattern. The subset of regular
+ * expression syntax which is supported is defined by POSIX P1003.2
+ * FILENAME EXPANSION rules.
+ *
+ * PARAMETERS
+ *
+ * char *pattern - The regular expression which we are supposed to
+ * match to.
+ * char *source - The source string which is to be compared to the
+ * regular expression pattern.
+ *
+ * RETURNS
+ *
+ * Returns non-zero if the source string matches the regular expression
+ * pattern specified, returns 0 otherwise.
+ *
+ */
+
+#ifdef __STDC__
+
+int wildmat(char *pattern, char *source)
+
+#else
+
+int wildmat(pattern, source)
+char *pattern; /* regular expression to match */
+char *source; /* source operand */
+
+#endif
+{
+ int last; /* last character matched */
+ int matched; /* !0 if a match occurred */
+ int reverse; /* !0 if sense of match is reversed */
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int wildmat() in wildmat.c\n");
+#endif
+ for (; *pattern; source++, pattern++) {
+ switch (*pattern) {
+ case '\\':
+ /* Literal match with following character */
+ pattern++;
+ /* FALLTHRU */
+ default:
+ if (*source != *pattern) {
+ return (0);
+ }
+ continue;
+ case '?':
+ /* Match anything. */
+ if (*source == '\0') {
+ return (0);
+ }
+ continue;
+ case '*':
+ /* Trailing star matches everything. */
+ return (*++pattern ? star(source, pattern) : 1);
+ case '[':
+ /* [^....] means inverse character class. */
+ if (reverse = pattern[1] == '^') {
+ pattern++;
+ }
+ for (last = 0400, matched = 0;
+ *++pattern && *pattern != ']'; last = *pattern) {
+ /* This next line requires a good C compiler. */
+ if (*pattern == '-'
+ ? *source <= *++pattern && *source >= last
+ : *source == *pattern) {
+ matched = 1;
+ }
+ }
+ if (matched == reverse) {
+ return (0);
+ }
+ continue;
+ }
+ }
+
+ /*
+ * For "tar" use, matches that end at a slash also work. --hoptoad!gnu
+ */
+ return (*source == '\0' || *source == '/');
+}
diff --git a/private/posix/programs/psxarc/archive.c b/private/posix/programs/psxarc/archive.c
new file mode 100644
index 000000000..e9869ceb3
--- /dev/null
+++ b/private/posix/programs/psxarc/archive.c
@@ -0,0 +1,118 @@
+//
+// In this file are routines to perform operations on archive files.
+// They do this by determining the type of the archive and calling a
+// corresponding type-specific routine.
+//
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <tar.h>
+#include <string.h>
+#include "buf.h"
+#include "psxarc.h"
+#include "tarhead.h"
+#include "cpio.h"
+#include "links.h"
+
+//
+// These should be in an include file.
+//
+
+extern void CpioList(), CpioRead(), CpioWrite();
+extern void TarList(), TarRead(), TarWrite();
+extern void cpio_itoa();
+
+void
+ListArchive(PBUF pb)
+{
+ PCPIO_HEAD cp;
+ PTAR_HEAD tp;
+
+ InitLinkList();
+
+ bfill(pb);
+ cp = (PCPIO_HEAD)pb->data;
+
+ if (0 == strncmp(cp->c_magic, MAGIC, strlen(MAGIC))) {
+ CpioList(pb);
+ return;
+ }
+
+ tp = (PTAR_HEAD)pb->data;
+
+ if (0 == strncmp(tp->s.magic, TMAGIC, strlen(TMAGIC))) {
+ TarList(pb);
+ return;
+ }
+ fprintf(stderr, "%s: unknown archive type\n", progname);
+ exit(4);
+}
+
+void
+WriteArchive(PBUF pb, int format, char **files, int count)
+{
+ InitLinkList();
+
+ if (FORMAT_CPIO == format) {
+ int len, i;
+ CPIO_HEAD h;
+
+ CpioWrite(pb, files, count);
+
+ // write the trailer.
+ (void)strncpy(h.c_magic, MAGIC, strlen(MAGIC));
+ cpio_itoa(C_ISREG, h.c_mode, sizeof(h.c_mode));
+ cpio_itoa(sizeof(LASTFILENAME), h.c_namesize,
+ sizeof(h.c_namesize));
+ cpio_itoa(0, h.c_filesize, sizeof(h.c_namesize));
+
+ for (i = 0; i < sizeof(h); ++i) {
+ bputc(pb, ((char *)&h)[i]);
+ }
+
+ // "sizeof" so we get the nul, too
+ len = sizeof(LASTFILENAME);
+ for (i = 0; i < len; ++i) {
+ bputc(pb, LASTFILENAME[i]);
+ }
+ bflush(pb);
+ return;
+ }
+ if (FORMAT_TAR == format) {
+ TarWrite(pb, files, count);
+
+ // a tar archive is terminated by two blocks of zeroes.
+ memset(pb->data, 0, sizeof(pb->data));
+ bflush(pb);
+ bflush(pb);
+ return;
+ }
+ // shouldn't get here.
+ exit(10);
+}
+
+void
+ReadArchive(PBUF pb)
+{
+ PCPIO_HEAD cp;
+ PTAR_HEAD tp;
+
+ InitLinkList();
+
+ bfill(pb);
+ cp = (PCPIO_HEAD)pb->data;
+
+ if (0 == strncmp(cp->c_magic, MAGIC, strlen(MAGIC))) {
+ CpioRead(pb);
+ return;
+ }
+
+ tp = (PTAR_HEAD)pb->data;
+
+ if (0 == strncmp(tp->s.magic, TMAGIC, strlen(TMAGIC))) {
+ TarRead(pb);
+ return;
+ }
+ fprintf(stderr, "%s: unknown archive type\n", progname);
+ exit(4);
+}
diff --git a/private/posix/programs/psxarc/buf.c b/private/posix/programs/psxarc/buf.c
new file mode 100644
index 000000000..da3f87afb
--- /dev/null
+++ b/private/posix/programs/psxarc/buf.c
@@ -0,0 +1,177 @@
+/*++
+
+ This file contains stuff for buffering input and output. We use
+ this buffering to allow us to deal with end-of-media conditions
+ on input or output streams.
+
+ Warning: at this time, these buffers don't get flushed automatically
+ on exit; bclose() must be called.
+
+--*/
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "buf.h"
+
+extern char *progname;
+
+static PBUF balloc(void);
+static void bfree(PBUF);
+
+//
+// Create a new buffer and associate it with a file. If the file
+// can't be opened, return NULL.
+//
+PBUF
+bopen(const char *file, int mode)
+{
+ PBUF pb;
+
+ pb = balloc();
+
+ if (-1 == (pb->fd = open(file, mode, 0666))) {
+ fprintf(stderr, "%s: open: ", progname);
+ perror(file);
+ exit(1);
+ }
+ pb->mode = mode;
+ pb->count = 0;
+ pb->offset = 0;
+
+ return pb;
+}
+
+//
+// Same as bopen, but from a file descriptor.
+//
+PBUF
+bfdopen(int fd, int mode)
+{
+ PBUF pb;
+
+ pb = balloc();
+
+ pb->fd = fd;
+ pb->mode = mode;
+ pb->count = 0;
+ pb->offset = 0;
+
+ return pb;
+}
+
+void
+bclose(PBUF pb)
+{
+ if (pb->mode & O_WRONLY && pb->offset != 0) {
+ bflush(pb);
+ }
+ (void)close(pb->fd);
+ bfree(pb);
+}
+
+int
+bgetc(PBUF pb)
+{
+ if (pb->offset == pb->count) {
+ bfill(pb);
+ }
+
+ // We don't worry about reaching EOF here; the caller has to
+ // know how many bytes are available and read only that many,
+
+ return pb->data[pb->offset++];
+}
+
+void
+bputc(PBUF pb, int c)
+{
+ pb->data[pb->offset++] = (char)c;
+ if (sizeof(pb->data) == ++pb->count) {
+ bflush(pb);
+ }
+}
+
+//
+// Set a buffer back to the beginning; that is, the next character
+// read will be the first one. Could be used on write buffers as well,
+// but not as likely.
+//
+void
+brewind(PBUF pb)
+{
+ pb->offset = 0;
+}
+
+//
+// balloc --
+// Allocate and initialize a buffer. A pointer to the new buffer
+// is returned. We set fd to -1 to help find out if people are
+// writing to a buffer before opening it.
+//
+static PBUF
+balloc()
+{
+ PBUF pb;
+
+ if (NULL == (pb = malloc(sizeof(BUF)))) {
+ fprintf(stderr, "%s: malloc: virtual memory exhausted\n",
+ progname);
+ exit(4);
+ }
+ return pb;
+}
+
+static void
+bfree(PBUF pb)
+{
+ free(pb);
+}
+
+//
+// write the contents of a buffer, dealing with end-of-media, if necessary.
+//
+void
+bflush(PBUF pb)
+{
+ int nbyte;
+
+ nbyte = write(pb->fd, pb->data, sizeof(pb->data));
+ if (-1 == nbyte) {
+ if (ENOSPC == errno) {
+ // end-of-media; do something about it.
+ } else {
+ fprintf(stderr, "%s: ", progname);
+ perror("write");
+ exit(1);
+ }
+ }
+ pb->count = 0;
+ pb->offset = 0;
+}
+
+//
+// fill a buffer with data, dealing with end-of-media, if necessary.
+//
+void
+bfill(PBUF pb)
+{
+ int nbyte;
+
+ nbyte = read(pb->fd, pb->data, sizeof(pb->data));
+ if (-1 == nbyte) {
+ fprintf(stderr, "%s: ", progname);
+ perror("read");
+ exit(2);
+ }
+ if (0 == nbyte) {
+ // we have reached the end of file. Give user opportunity
+ // to replace the media, and fill the rest of this
+ // buffer XXX.mjb
+ return;
+ }
+ pb->count = nbyte;
+ pb->offset = 0;
+}
diff --git a/private/posix/programs/psxarc/buf.h b/private/posix/programs/psxarc/buf.h
new file mode 100644
index 000000000..d13b1c4c1
--- /dev/null
+++ b/private/posix/programs/psxarc/buf.h
@@ -0,0 +1,27 @@
+#ifndef _BUF_
+#define _BUF_
+
+//
+// Extern declarations and so forth for buffer-management routines.
+//
+
+typedef struct _BUF {
+ char data[512]; // tar depends on this 512
+ int offset; // offset into data
+ int count; // how many bytes available?
+ int fd;
+ int mode;
+} BUF, *PBUF;
+
+PBUF bopen(const char *file, int mode);
+PBUF bfdopen(int fd, int mode);
+void bclose(PBUF pb);
+int bread(PBUF pb, void *buf, int len);
+int bwrite(PBUF pb, void *buf, int len);
+void bfill(PBUF pb);
+void bflush(PBUF pb);
+void brewind(PBUF pb);
+int bgetc(PBUF pb);
+void bputc(PBUF pb, int c);
+
+#endif // _BUF_
diff --git a/private/posix/programs/psxarc/cpio.c b/private/posix/programs/psxarc/cpio.c
new file mode 100644
index 000000000..4ac5baafa
--- /dev/null
+++ b/private/posix/programs/psxarc/cpio.c
@@ -0,0 +1,318 @@
+//
+// Stuff to deal with cpio-format files
+//
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <string.h>
+
+#include "cpio.h"
+#include "buf.h"
+#include "psxarc.h"
+#include "links.h"
+
+extern int errno;
+extern int fVerbose;
+
+static void cpio_dodir(PBUF pb, char *pchfile, struct stat *psb);
+
+//
+// Convert string pch of length len from octal and return the value.
+//
+static int
+cpio_atoi(char *pch, int len)
+{
+ int num = 0, i;
+
+ for (i = 0; i < len; ++i) {
+ num = num * 8 + (pch[i] - '0');
+ }
+ return num;
+}
+
+void
+CpioList(PBUF pb)
+{
+ int nbytes;
+ int namesize, filesize;
+ int i;
+ static char pathname[PATH_MAX + NAME_MAX + 2];
+ CPIO_HEAD x;
+
+ for (;;) {
+ //
+ // read the cpio header
+ //
+
+ for (i = 0; i < sizeof(x); ++i) {
+ ((char *)&x)[i] = bgetc(pb);
+ }
+ if (0 != strncmp(x.c_magic, MAGIC, strlen(MAGIC))) {
+ fprintf(stderr,
+ "%s: this doesn't look like a cpio archive\n",
+ progname);
+ exit(1);
+ }
+
+ namesize = cpio_atoi(x.c_namesize, sizeof(x.c_namesize));
+ filesize = cpio_atoi(x.c_filesize, sizeof(x.c_filesize));
+
+ for (i = 0; i < namesize; ++i) {
+ // nb: namesize includes the null
+ pathname[i] = bgetc(pb);
+ }
+ if (0 == strcmp(pathname, LASTFILENAME)) {
+ break;
+ }
+
+ printf("%s\n", pathname);
+
+ // skip the file data
+ for (i = 0; i < filesize; ++i) {
+ (void)bgetc(pb);
+ }
+ }
+}
+
+void
+CpioRead(PBUF pb)
+{
+ int fdout;
+ int mode;
+ int i;
+ int namesize, filesize;
+ static CPIO_HEAD x;
+ static char pathname[PATH_MAX + NAME_MAX + 2];
+
+ for (;;) {
+ //
+ // read the cpio header
+ //
+
+ for (i = 0; i < sizeof(x); ++i) {
+ ((char *)&x)[i] = bgetc(pb);
+ }
+
+ if (0 != strncmp(x.c_magic, MAGIC, strlen(MAGIC))) {
+ fprintf(stderr,
+ "%s: this doesn't look like a cpio archive\n",
+ progname);
+ exit(1);
+ }
+
+ namesize = cpio_atoi(x.c_namesize, sizeof(x.c_namesize));
+ filesize = cpio_atoi(x.c_filesize, sizeof(x.c_filesize));
+
+ for (i = 0; i < namesize; ++i) {
+ // nb: namesize includes the null
+ pathname[i] = bgetc(pb);
+ }
+ if (0 == strcmp(pathname, LASTFILENAME)) {
+ break;
+ }
+
+ if (fVerbose) {
+ printf("%s\n", pathname);
+ }
+
+ mode = cpio_atoi(x.c_mode, sizeof(x.c_mode));
+
+ if (mode & C_ISDIR) {
+ mkdir(pathname, 0777);
+ } else if (mode & C_ISFIFO) {
+ mkfifo(pathname, 0666);
+ } else if (mode & C_ISREG) {
+ fdout = open(pathname, O_WRONLY | O_CREAT, 0666);
+ if (-1 == fdout) {
+ fprintf(stderr, "%s: open: ", progname);
+ perror(pathname);
+
+ // we could continue, but we'd have to be sure
+ // to skip this file's data.
+
+ exit(1);
+ }
+ for (i = 0; i < filesize; ++i) {
+ char c;
+ c = (char)bgetc(pb);
+ (void)write(fdout, &c, 1);
+ --filesize;
+ }
+ (void)close(fdout);
+ } else if (mode & C_ISLNK) {
+ // XXX.mjb: symbolic link
+ } else {
+ fprintf(stderr, "%s: unknown mode 0%o\n", progname, mode);
+ exit(4);
+ }
+ }
+}
+
+void
+cpio_itoa(int i, char *pch, int len)
+{
+ int j;
+ char buf[20];
+
+ sprintf(buf, "%o", i);
+
+ j = strlen(buf);
+ if (j > len) {
+ printf("itoa: not enough room in buf: need %d, have %d\n",
+ j, len);
+ exit(3);
+ }
+
+ memset(pch, '0', len);
+ strncpy(&pch[len - j], buf, strlen(buf));
+}
+
+void
+CpioWrite(PBUF pb, char **files, int count)
+{
+ CPIO_HEAD h;
+ struct stat statbuf;
+ int i, len;
+ int fdin;
+
+ (void)strncpy(h.c_magic, MAGIC, strlen(MAGIC));
+
+ while (count > 0) {
+ if (fVerbose) {
+ printf("%s\n", *files);
+ }
+ if (-1 == (fdin = open(*files, O_RDONLY))) {
+ fprintf(stderr, "%s: open: ");
+ perror(*files);
+ exit(1);
+ }
+ if (-1 == fstat(fdin, &statbuf)) {
+ perror("fstat");
+ exit(1);
+ }
+
+ cpio_itoa(strlen(*files) + 1, h.c_namesize, sizeof(h.c_namesize));
+ cpio_itoa(statbuf.st_dev, h.c_dev, sizeof(h.c_dev));
+ cpio_itoa(statbuf.st_ino, h.c_ino, sizeof(h.c_ino));
+ cpio_itoa(statbuf.st_uid, h.c_uid, sizeof(h.c_uid));
+ cpio_itoa(statbuf.st_gid, h.c_gid, sizeof(h.c_gid));
+ cpio_itoa(statbuf.st_nlink, h.c_nlink, sizeof(h.c_nlink));
+ cpio_itoa(statbuf.st_mtime, h.c_mtime, sizeof(h.c_mtime));
+
+ if (S_ISDIR(statbuf.st_mode)) {
+ cpio_itoa(C_ISDIR, h.c_mode, sizeof(h.c_mode));
+ cpio_itoa(0, h.c_filesize, sizeof(h.c_filesize));
+
+ // write the header
+
+ for (i = 0; i < sizeof(h); ++i) {
+ bputc(pb, ((char *)&h)[i]);
+ }
+
+ // write the directory name
+
+ len = strlen(*files) + 1; // the nul, too
+ for (i = 0; i < len; ++i) {
+ bputc(pb, (*files)[i]);
+ }
+
+ // write the directory contents
+ cpio_dodir(pb, *files, &statbuf);
+
+ count--;
+ files++;
+ continue;
+ }
+ if (S_ISFIFO(statbuf.st_mode)) {
+ cpio_itoa(C_ISFIFO, h.c_mode, sizeof(h.c_mode));
+ cpio_itoa(0, h.c_filesize, sizeof(h.c_filesize));
+ } else if (S_ISREG(statbuf.st_mode)) {
+ cpio_itoa(C_ISREG, h.c_mode, sizeof(h.c_mode));
+ cpio_itoa(statbuf.st_size, h.c_filesize, sizeof(h.c_filesize));
+ } else {
+ printf("I'm not prepared to deal with the file type "
+ "of %s\n", *files);
+ exit(2);
+ }
+
+ // write the cpio header
+ for (i = 0; i < sizeof(h); ++i) {
+ bputc(pb, ((char *)&h)[i]);
+ }
+
+ // write the filename
+
+ len = strlen(*files) + 1; // the nul, too
+ for (i = 0; i < len; ++i) {
+ bputc(pb, (*files)[i]);
+ }
+
+ while (statbuf.st_size > 0) {
+ char b;
+ (void)read(fdin, &b, 1);
+ bputc(pb, b);
+ --statbuf.st_size;
+ }
+
+ close(fdin);
+ count--;
+ files++;
+ }
+
+#if 0
+ printf("", count); // null function call to work around
+ // mips code generator problem
+#endif
+}
+
+static void
+cpio_dodir(PBUF pb, char *pchfile, struct stat *psb)
+{
+ DIR *dp;
+ struct dirent *dirent;
+ char *pch;
+
+ dp = opendir(pchfile);
+ if (NULL == dp) {
+ fprintf(stderr, "%s: opendir: ", progname);
+ perror(pchfile);
+ return;
+ }
+ while (NULL != (dirent = readdir(dp))) {
+ if ('.' == dirent->d_name[0] &&
+ ('\0' == dirent->d_name[1] ||
+ 0 == strcmp(dirent->d_name, ".."))) {
+ continue;
+ }
+
+ //
+ // Recurse. We append the file name read from the directory
+ // to the directory name we were given and call CpioWrite to
+ // put it on the tape. It could be a directory, so we could
+ // end up back here. This means that we must allocate the
+ // space for the pathname dynamically. This seems like it
+ // will be a big time-waster.
+ //
+
+ // strlen + 2: one extra for '/', one extra for nul.
+ pch = malloc(strlen(pchfile) + strlen(dirent->d_name) + 2);
+ if (NULL == pch) {
+ fprintf(stderr, "%s: virtual memory exhausted\n",
+ progname);
+ exit(4);
+ }
+ strcpy(pch, pchfile);
+ strcat(pch, "/");
+ strcat(pch, dirent->d_name);
+
+ CpioWrite(pb, &pch, 1);
+
+ free(pch);
+ }
+ (void)closedir(dp);
+}
diff --git a/private/posix/programs/psxarc/cpio.h b/private/posix/programs/psxarc/cpio.h
new file mode 100644
index 000000000..82708e83e
--- /dev/null
+++ b/private/posix/programs/psxarc/cpio.h
@@ -0,0 +1,50 @@
+#ifndef _CPIO_
+#define _CPIO_
+
+typedef struct _CPIO_HEAD {
+ char c_magic[6];
+ char c_dev[6];
+ char c_ino[6];
+ char c_mode[6];
+ char c_uid[6];
+ char c_gid[6];
+ char c_nlink[6];
+ char c_rdev[6];
+ char c_mtime[11];
+ char c_namesize[6];
+ char c_filesize[11];
+} CPIO_HEAD, *PCPIO_HEAD;
+
+#define MAGIC "070707"
+#define LASTFILENAME "TRAILER!!!"
+
+//
+// File permissions, for c_mode
+//
+#define C_IRUSR 000400
+#define C_IWUSR 000200
+#define C_IXUSR 000100
+#define C_IRGRP 000040
+#define C_IWGRP 000020
+#define C_IXGRP 000010
+#define C_IROTH 000004
+#define C_IWOTH 000002
+#define C_IXOTH 000001
+
+#define C_ISUID 004000
+#define C_ISGID 002000
+#define C_ISVTX 001000
+
+//
+// File type, also in c_mode
+//
+#define C_ISDIR 040000
+#define C_ISFIFO 010000
+#define C_ISREG 0100000
+#define C_ISBLK 060000
+#define C_ISCHR 020000
+#define C_ISCTG 0110000
+#define C_ISLNK 0120000
+#define C_ISSOCK 0140000
+
+#endif // _CPIO_
diff --git a/private/posix/programs/psxarc/getopt.c b/private/posix/programs/psxarc/getopt.c
new file mode 100644
index 000000000..2d471d1c6
--- /dev/null
+++ b/private/posix/programs/psxarc/getopt.c
@@ -0,0 +1,78 @@
+/* got this off net.sources */
+#include <stdio.h>
+#include <string.h>
+#include "getopt.h"
+
+/*
+ * get option letter from argument vector
+ */
+int
+ opterr = 1, // should error messages be printed?
+ optind = 1, // index into parent argv vector
+ optopt; // character checked for validity
+char
+ *optarg; // argument associated with option
+
+#define EMSG ""
+
+char *progname; // may also be defined elsewhere
+
+static void
+error(char *pch)
+{
+ if (!opterr) {
+ return; // without printing
+ }
+ fprintf(stderr, "%s: %s: %c\n",
+ (NULL != progname) ? progname : "getopt", pch, optopt);
+}
+
+int
+getopt(int argc, char **argv, char *ostr)
+{
+ static char *place = EMSG; /* option letter processing */
+ register char *oli; /* option letter list index */
+
+ if (!*place) {
+ // update scanning pointer
+ if (optind >= argc || *(place = argv[optind]) != '-' || !*++place) {
+ return EOF;
+ }
+ if (*place == '-') {
+ // found "--"
+ ++optind;
+ return EOF;
+ }
+ }
+
+ /* option letter okay? */
+ if ((optopt = (int)*place++) == (int)':'
+ || !(oli = strchr(ostr, optopt))) {
+ if (!*place) {
+ ++optind;
+ }
+ error("illegal option");
+ return BADCH;
+ }
+ if (*++oli != ':') {
+ /* don't need argument */
+ optarg = NULL;
+ if (!*place)
+ ++optind;
+ } else {
+ /* need an argument */
+ if (*place) {
+ optarg = place; /* no white space */
+ } else if (argc <= ++optind) {
+ /* no arg */
+ place = EMSG;
+ error("option requires an argument");
+ return BADCH;
+ } else {
+ optarg = argv[optind]; /* white space */
+ }
+ place = EMSG;
+ ++optind;
+ }
+ return optopt; // return option letter
+}
diff --git a/private/posix/programs/psxarc/getopt.h b/private/posix/programs/psxarc/getopt.h
new file mode 100644
index 000000000..2fbfff635
--- /dev/null
+++ b/private/posix/programs/psxarc/getopt.h
@@ -0,0 +1,13 @@
+#ifndef _GETOPT_
+#define _GETOPT_
+
+int getopt(int argc, char **argv, char *optstring);
+
+extern char *optarg; // returned arg to go with this option
+extern int optind; // index to next argv element to process
+extern int opterr; // should error messages be printed?
+extern int optopt; //
+
+#define BADCH ('?')
+
+#endif // _GETOPT
diff --git a/private/posix/programs/psxarc/links.c b/private/posix/programs/psxarc/links.c
new file mode 100644
index 000000000..6692e0411
--- /dev/null
+++ b/private/posix/programs/psxarc/links.c
@@ -0,0 +1,64 @@
+#include <sys/stat.h>
+#include <string.h>
+#include <stdlib.h>
+#include "links.h"
+
+static LIST_ENTRY LinkList;
+
+void
+InitLinkList(void)
+{
+ InitializeListHead(&LinkList);
+}
+
+PLINKFILE
+GetLinkByIno(ino_t ino)
+{
+ PLINKFILE p;
+
+ for (p = (void *)LinkList.Flink; p != (void *)&LinkList;
+ p = (void *)p->links.Flink) {
+ if (p->ino == ino) {
+ return p;
+ }
+ }
+ return NULL;
+}
+
+PLINKFILE
+GetLinkByName(char *pchName)
+{
+ PLINKFILE p;
+
+ for (p = (PLINKFILE)LinkList.Flink; p != (PLINKFILE)&LinkList;
+ p = (void *)p->links.Flink) {
+ if (0 == strcmp(p->name, pchName)) {
+ return p;
+ }
+ }
+ return NULL;
+}
+
+void
+AddLinkList(struct stat *pstat, char *pchName)
+{
+ PLINKFILE pl;
+ char *pch;
+
+ pl = malloc(sizeof(*pl));
+ if (NULL == pl) {
+ return;
+ }
+ pch = strdup(pchName);
+ if (NULL == pch) {
+ free(pl);
+ return;
+ }
+
+ pl->nlink = pstat->st_nlink;
+ pl->name = pch;
+ pl->dev = pstat->st_dev;
+ pl->ino = pstat->st_ino;
+
+ InsertTailList(&LinkList, &pl->links);
+}
diff --git a/private/posix/programs/psxarc/links.h b/private/posix/programs/psxarc/links.h
new file mode 100644
index 000000000..b73dc0e92
--- /dev/null
+++ b/private/posix/programs/psxarc/links.h
@@ -0,0 +1,39 @@
+#include <sys/types.h>
+
+typedef struct _LIST_ENTRY {
+ struct _LIST_ENTRY *Flink;
+ struct _LIST_ENTRY *Blink;
+} LIST_ENTRY, *PLIST_ENTRY;
+
+typedef struct _LINKFILE {
+ LIST_ENTRY links;
+ int nlink;
+ char *name;
+ dev_t dev;
+ ino_t ino;
+} LINKFILE, *PLINKFILE;
+
+#define InsertTailList(ListHead, Entry) \
+ (Entry)->Flink = (ListHead);\
+ (Entry)->Blink = (ListHead)->Blink;\
+ (ListHead)->Blink->Flink = (Entry);\
+ (ListHead)->Blink = (Entry)
+
+#define RemoveEntryList(Entry) {\
+ PLIST_ENTRY _EX_Entry;\
+ _EX_Entry = (Entry);\
+ _EX_Entry->Blink->Flink = _EX_Entry->Flink;\
+ _EX_Entry->Flink->Blink = _EX_Entry->Blink;\
+ }
+
+#define InitializeListHead(ListHead) \
+ ((ListHead)->Flink = (ListHead)->Blink = (ListHead))
+
+#define IsListEmpty(ListHead) \
+ ((ListHead)->Flink == (ListHead))
+
+extern PLINKFILE GetLinkByName(char *);
+extern PLINKFILE GetLinkByIno(ino_t);
+struct stat;
+extern void AddLinkList(struct stat *, char *);
+extern void InitLinkList(void);
diff --git a/private/posix/programs/psxarc/makefile b/private/posix/programs/psxarc/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/posix/programs/psxarc/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/posix/programs/psxarc/psxarc.c b/private/posix/programs/psxarc/psxarc.c
new file mode 100644
index 000000000..98ffa0dfa
--- /dev/null
+++ b/private/posix/programs/psxarc/psxarc.c
@@ -0,0 +1,124 @@
+/*++
+
+psxarc - a program to do minimal minipulation and extraction of POSIX-type
+ tar and cpio archives. Certainly not as good as real tar and cpio.
+
+--*/
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include "getopt.h"
+#include "buf.h"
+#include "psxarc.h"
+
+char *progname = "psxarc";
+
+char *pchArchive;
+PBUF pbArchive;
+
+int fRead, fWrite; // what to do; neither == list archive
+int fVerbose; // to be, or not to be
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: %s [-hrv] [-f archive]\n", progname);
+ fprintf(stderr,
+ "\t%s -w [-f archive] [-x format] files\n", progname);
+}
+
+int
+main(int argc, char **argv)
+{
+ int c;
+ char *pchOpts = "hf:rvwx:";
+ int format = FORMAT_DEFAULT; // to write tar or cpio?
+
+ // parse options
+ while (-1 != (c = getopt(argc, argv, pchOpts))) {
+ switch (c) {
+ case 'f':
+ pchArchive = optarg;
+ break;
+ case 'h':
+ usage();
+ fprintf(stderr, "-h:\t help\n");
+ fprintf(stderr, "-r:\t read archive file\n");
+ fprintf(stderr, "-w:\t write archive file\n");
+ fprintf(stderr, "-f:\t specify archive file, default stdio\n");
+ fprintf(stderr, "-v:\t be verbose\n");
+ fprintf(stderr, "-x:\t use format, tar or cpio\n");
+ return 0;
+ case 'r':
+ ++fRead;
+ break;
+ case 'w':
+ ++fWrite;
+ break;
+ case 'v':
+ ++fVerbose;
+ break;
+ case 'x':
+ if (0 == strcmp(optarg, "tar")) {
+ format = FORMAT_TAR;
+ } else if (0 == strcmp(optarg, "cpio")) {
+ format = FORMAT_CPIO;
+ } else {
+ fprintf(stderr, "%s: unknown format %s\n",
+ progname, optarg);
+ return 4;
+ }
+ break;
+ case BADCH:
+ default:
+ usage();
+ return 1;
+ }
+ }
+ if (fRead && fWrite) {
+ fprintf(stderr, "%s: -r excludes -w\n", progname);
+ return 1;
+ }
+ if (NULL != pchArchive) {
+ int mode;
+ if (fWrite) {
+ // write to archive file instead of stdout
+ mode = O_WRONLY | O_CREAT;
+ } else {
+ // either -r (read) or list
+ mode = O_RDONLY;
+ }
+ pbArchive = bopen(pchArchive, mode);
+
+ } else {
+ if (fRead) {
+ pbArchive = bfdopen(fileno(stdin), O_RDONLY);
+ } else if (fWrite) {
+ pbArchive = bfdopen(fileno(stdout), O_WRONLY);
+ }
+ }
+ if (!fRead && !fWrite) {
+ // list the archive
+ ListArchive(pbArchive);
+ return 0;
+ }
+ if (fRead) {
+ ReadArchive(pbArchive);
+ return 0;
+ }
+
+ if (optind == argc) {
+ usage();
+ return 1;
+ }
+
+ if (FORMAT_DEFAULT == format) {
+ fprintf(stderr, "%s: warning: using tar format\n", progname);
+ format = FORMAT_TAR;
+ }
+ WriteArchive(pbArchive, format, &argv[optind], argc - optind);
+ return 0;
+}
diff --git a/private/posix/programs/psxarc/psxarc.h b/private/posix/programs/psxarc/psxarc.h
new file mode 100644
index 000000000..777f8411d
--- /dev/null
+++ b/private/posix/programs/psxarc/psxarc.h
@@ -0,0 +1,16 @@
+#ifndef _PSXARC_
+#define _PSXARC_
+
+extern char *progname;
+
+void ReadArchive(PBUF pb);
+void ListArchive(PBUF pb);
+void WriteArchive(PBUF pb, int format, char **files, int count);
+
+void *readBlock(int fd);
+
+#define FORMAT_DEFAULT 0
+#define FORMAT_CPIO 1
+#define FORMAT_TAR 2
+
+#endif // _PSXARC_
diff --git a/private/posix/programs/psxarc/sources b/private/posix/programs/psxarc/sources
new file mode 100644
index 000000000..96447df73
--- /dev/null
+++ b/private/posix/programs/psxarc/sources
@@ -0,0 +1,29 @@
+WIMPYMASM=1
+
+MAJORCOMP=posix
+MINORCOMP=client
+
+TARGETNAME=psxarc
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+INCLUDES=\nt\public\sdk\inc\posix
+
+SOURCES= \
+ psxarc.c \
+ buf.c \
+ archive.c \
+ tar.c \
+ links.c \
+ cpio.c \
+ getopt.c
+
+C_DEFINES= -D_POSIX_
+UMTYPE=posix
+UMAPPL=psxarc
+UMLIBS= \
+ obj\*\psxarc.lib
+OPTIONAL_UMTEST=
+UMBASE=0xa00000
+
+386_STDCALL=0
diff --git a/private/posix/programs/psxarc/tar.c b/private/posix/programs/psxarc/tar.c
new file mode 100644
index 000000000..98706eff5
--- /dev/null
+++ b/private/posix/programs/psxarc/tar.c
@@ -0,0 +1,457 @@
+//
+// Stuff to deal with tar-format files
+//
+
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <tar.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "buf.h"
+#include "psxarc.h"
+#include "tarhead.h"
+#include "links.h"
+
+static unsigned long round_up(int, int);
+static int tarAtoi(char *);
+static void tarItoa(long, char *, size_t);
+static void tar_dodir(PBUF pb, char *pchfile, struct stat *psb);
+static char *modestring(PTAR_HEAD);
+extern int fVerbose;
+
+void
+TarRead(PBUF pb)
+{
+ char name[155 + 1 + 100 + 1]; // (prefix '/' name '\0')
+ char linkname[TNAMSIZ + 1];
+ unsigned long size, i;
+ int fdout;
+ PTAR_HEAD buf = (PTAR_HEAD)pb->data;
+
+
+ if (0 != strcmp(buf->s.magic, TMAGIC)) {
+ fprintf(stderr, "%s: bad magic number\n", progname);
+ exit(1);
+ }
+
+ name[sizeof(name) - 1] = '\0';
+
+ do {
+ (void)strncpy(name, buf->s.prefix, 155);
+ // make sure 'name' is null-terminated.
+ name[155] = '\0';
+ if ('\0' != name[0]) {
+ (void)strcat(name, "/");
+ }
+ (void)strncat(name, buf->s.name, 100);
+
+ if (fVerbose) {
+ printf("%s\n", name);
+ }
+
+ if (DIRTYPE == buf->s.typeflag) {
+ if (-1 == mkdir(name, 0777)) {
+ fprintf(stderr, "%s: mkdir: ", progname);
+ perror(name);
+ }
+ bfill(pb);
+ continue;
+ }
+ if (FIFOTYPE == buf->s.typeflag) {
+ if (-1 == mkfifo(name, 0666)) {
+ fprintf(stderr, "%s: mkfifo: ", progname);
+ perror(name);
+ }
+ bfill(pb);
+ continue;
+ }
+ if (LNKTYPE == buf->s.typeflag) {
+ strncpy(linkname, buf->s.linkname, sizeof(linkname));
+ if (-1 == link(linkname, name)) {
+ fprintf(stderr, "%s: link %s, %s: ",
+ progname, linkname, name);
+ perror("");
+ continue;
+ }
+ }
+
+ // regular file
+ if (-1 == (fdout = open(name, O_WRONLY | O_CREAT, 0666))) {
+ fprintf(stderr, "%s: open: ", progname);
+ perror(name);
+ continue;
+ }
+
+ size = tarAtoi(buf->s.size);
+
+ if (size > 0) {
+ bfill(pb);
+
+ for (i = 0; i < size; ++i) {
+ int c;
+
+ c = bgetc(pb);
+ write(fdout, &c, 1);
+ }
+ }
+ (void)close(fdout);
+
+ bfill(pb);
+ } while (0 != buf->s.magic[0]);
+}
+
+//
+// TarWrite -- calls tar_dodir for directories, which calls back here.
+//
+
+void
+TarWrite(PBUF pb, char **ppchFiles, int count)
+{
+ PTAR_HEAD pt;
+ auto struct stat statbuf;
+ int fdin;
+ int i;
+ unsigned chksum;
+
+ // We reach into the buffer routines until it's time to write.
+ // Brutal but effective.
+
+ pt = (PTAR_HEAD)pb->data;
+
+ while (count > 0) {
+ int len;
+
+ memset(pt, 0, sizeof(*pt));
+ strcpy(pt->s.magic, TMAGIC);
+ strncpy(pt->s.version, TVERSION, TVERSLEN);
+
+ if (fVerbose) {
+ fprintf(stderr, "%s\n", *ppchFiles);
+ }
+ if (-1 == stat(*ppchFiles, &statbuf)) {
+ fprintf(stderr, "%s: stat: ");
+ perror(*ppchFiles);
+ continue;
+ }
+ len = strlen(*ppchFiles);
+ if (len > TNAMSIZ + 155 + 1) {
+ // the filename just won't fit. Do something
+ // reasonable.
+ } else if (len <= TNAMSIZ) {
+ strncpy(pt->s.name, *ppchFiles, TNAMSIZ);
+ } else {
+ char *pch;
+
+ // We try to put as much of the filename as will fit
+ // into the 'name' portion, and the rest goes in
+ // the prefix. To do this, we start 101 characters
+ // from the end; if that character is a slash, we
+ // split the string there. If it's not, we split the
+ // string at the next slash to the right.
+
+ pch = *ppchFiles + (len - TNAMSIZ - 1);
+ if ('/' != *pch) {
+ pch = strchr(pch, '/');
+ if (NULL == pch) {
+ // XXX.mjb: This filename has a trailing
+ // component more than 100 chars
+ // long. Do something reasonable.
+ --count;
+ ++ppchFiles;
+ continue;
+ }
+ }
+ *pch = '\0';
+ strncpy(pt->s.name, pch + 1, TNAMSIZ);
+ strncpy(pt->s.prefix, *ppchFiles, 155);
+ }
+
+ //
+ // XXX.mjb: this assumes tar mode bits are the same as
+ // the POSIX implementation's mode bits. Should really
+ // call a function to convert between.
+ //
+ tarItoa(statbuf.st_mode & 0777, pt->s.mode,
+ sizeof(pt->s.mode));
+
+#if 0
+ tarItoa(statbuf.st_uid, pt->s.uid, sizeof(pt->s.uid));
+ tarItoa(statbuf.st_gid, pt->s.gid, sizeof(pt->s.gid));
+ tarItoa(statbuf.st_mtime, pt->s.mtime, sizeof(pt->s.mtime));
+#endif
+
+ if (S_ISDIR(statbuf.st_mode)) {
+ pt->s.typeflag = DIRTYPE;
+ memset(pt->s.size, '0', sizeof(pt->s.size));
+ // put the directory entry on the tape
+ bflush(pb);
+
+ // put the directory contents on tape
+ tar_dodir(pb, *ppchFiles, &statbuf);
+
+ ++ppchFiles;
+ --count;
+ continue;
+ }
+ if (S_ISFIFO(statbuf.st_mode)) {
+ pt->s.typeflag = FIFOTYPE;
+ memset(pt->s.size, '0', sizeof(pt->s.size));
+ bflush(pb);
+ ++ppchFiles;
+ --count;
+ continue;
+ }
+ if (statbuf.st_nlink > 1) {
+ PLINKFILE p;
+
+ if (NULL != (p = GetLinkByIno(statbuf.st_ino))) {
+ pt->s.typeflag = LNKTYPE;
+ memset(pt->s.size, '0', sizeof(pt->s.size));
+ strncpy(pt->s.linkname, p->name, TNAMSIZ);
+ bflush(pb);
+
+ ++ppchFiles;
+ --count;
+ continue;
+ }
+
+ AddLinkList(&statbuf, *ppchFiles);
+ }
+
+ pt->s.typeflag = REGTYPE;
+ tarItoa((int)statbuf.st_size, pt->s.size, sizeof(pt->s.size));
+
+ //
+ // compute the checksum for the header
+ //
+
+ memset(pt->s.chksum, ' ', sizeof(pt->s.chksum));
+ for (i = 0, chksum = 0; i < sizeof(pt->buf); ++i) {
+ chksum += pt->buf[i];
+ }
+ tarItoa(chksum, pt->s.chksum, sizeof(pt->s.chksum));
+
+ fdin = open(*ppchFiles, O_RDONLY, 0);
+ if (-1 == fdin) {
+ fprintf(stderr, "%s: open: ");
+ perror(*ppchFiles);
+ continue;
+ }
+
+ // write the header
+ bflush(pb);
+
+ if (0 == statbuf.st_size) {
+ //
+ // special case: don't write any data blocks.
+ //
+ close(fdin);
+ ++ppchFiles;
+ --count;
+ continue;
+ }
+
+ // copy the file data
+ // XXX.mjb: what should happen here if we find that we can't
+ // read the number of bytes we thought we'd be able to
+ // read? (The file could change size, or some kind
+ // of error could occur.) We can't leave the data too
+ // small, or we'll hose the rest of the tar file. So
+ // we write extra blocks of zeroes. What if the file
+ // turns out to be longer than expected? Print a
+ // warning and continue?
+
+ memset(pb->data, 0, sizeof(pb->data));
+ while (statbuf.st_size > 0) {
+ int nbytes;
+ char c;
+
+ nbytes = read(fdin, &c, 1);
+ if (-1 == nbytes) {
+ // error occurs before we have all the data.
+ }
+ bputc(pb, c);
+ --statbuf.st_size;
+ }
+ bflush(pb);
+ close(fdin);
+
+ ++ppchFiles;
+ --count;
+ }
+}
+
+void
+TarList(PBUF pb)
+{
+ PTAR_HEAD pt;
+ char name[155 + 1 + 100 + 1]; // "prefix / name \0"
+ unsigned long size, i;
+ char *pch;
+
+ pt = (PTAR_HEAD)pb->data;
+
+ do {
+ (void)strncpy(name, pt->s.prefix, 155);
+ name[155] = '\0';
+ if ('\0' != name[0]) {
+ (void)strcat(name, "/");
+ }
+ (void)strncat(name, pt->s.name, 100);
+
+ size = tarAtoi(pt->s.size);
+
+ if (!fVerbose) {
+ printf("%s\n", name);
+ } else {
+ pch = modestring(pt);
+ printf("%s %6ld %s\n", pch, size, name);
+ }
+
+ size = round_up(size, 512);
+ for (i = 0; i < size; ++i) {
+ bfill(pb);
+ }
+ bfill(pb);
+ } while (0 != pt->s.magic[0]);
+}
+
+//
+// tarAtof -- translate tar-style octal strings to decimal
+//
+static int
+tarAtoi(char *pch)
+{
+ int num = 0;
+
+ while ('\0' != *pch && ' ' != *pch) {
+ num = num * 8 + (*pch - '0');
+ ++pch;
+ }
+ return num;
+}
+
+//
+// tarItoa -- for writing numeric fields in tar headers.
+void
+tarItoa(long i, char *pch, size_t len)
+{
+ // XXX.mjb: should check width < len
+ sprintf(pch, "%-o", i);
+}
+
+//
+// round_up -- round num up to the nearest multiple of m.
+//
+unsigned long
+round_up(int num, int m)
+{
+ return (num + (m - 1))/m;
+}
+
+static void
+tar_dodir(
+ PBUF pb, // the tar image file, being written
+ char *pchfile, // the directory file to be added to the archv
+ struct stat *psb // result of stat on the directory file.
+ )
+{
+ DIR *dp;
+ struct dirent *dirent;
+ char *pch;
+
+ dp = opendir(pchfile);
+ if (NULL == dp) {
+ fprintf(stderr, "%s: opendir: ", progname);
+ perror(pchfile);
+ return;
+ }
+ while (NULL != (dirent = readdir(dp))) {
+ if ('.' == dirent->d_name[0] &&
+ ('\0' == dirent->d_name[1] ||
+ 0 == strcmp(dirent->d_name, ".."))) {
+ continue;
+ }
+
+ //
+ // Recurse. We append the file name read from the directory
+ // to the directory name we were given and call TarWrite to
+ // put it on the tape. It could be a directory, so we could
+ // end up back here. This means that we must allocate the
+ // space for the pathname dynamically. This seems like it
+ // will be a big time-waster.
+ //
+
+ // strlen + 2: one extra for '/', one extra for null.
+ pch = malloc(strlen(pchfile) + strlen(dirent->d_name) + 2);
+ if (NULL == pch) {
+ fprintf(stderr, "%s: virtual memory exhausted\n",
+ progname);
+ exit(4);
+ }
+ strcpy(pch, pchfile);
+ strcat(pch, "/");
+ strcat(pch, dirent->d_name);
+
+ TarWrite(pb, &pch, 1);
+ free(pch);
+ }
+ (void)closedir(dp);
+}
+
+static char *
+modestring(PTAR_HEAD pt)
+{
+ static char sb[11];
+ unsigned long mask, mode;
+ char *rwx = "rwxrwxrwx";
+ char *string;
+
+ sb[11] = '\0';
+ string = sb;
+
+ switch (pt->s.typeflag) {
+ case LNKTYPE:
+ case REGTYPE:
+ string[0] = '-';
+ break;
+ case DIRTYPE:
+ string[0] = 'd';
+ break;
+ case FIFOTYPE:
+ string[0] = 'f';
+ break;
+ case SYMTYPE:
+ string[0] = 'l';
+ break;
+ case BLKTYPE:
+ string[0] = 'b';
+ break;
+ case CHRTYPE:
+ string[0] = 'c';
+ break;
+ case CONTTYPE:
+ string[0] = '=';
+ break;
+ default:
+ fprintf(stderr, "modestring shouldn't get here\n");
+ }
+ string++;
+
+ mode = tarAtoi(pt->s.mode);
+
+ for (mask = 0400; mask != 0; mask >>= 1) {
+ if (mode & mask) {
+ *string++ = *rwx++;
+ } else {
+ *string++ = '-';
+ rwx++;
+ }
+ }
+
+ return sb;
+}
diff --git a/private/posix/programs/psxarc/tarhead.h b/private/posix/programs/psxarc/tarhead.h
new file mode 100644
index 000000000..9f7affcbe
--- /dev/null
+++ b/private/posix/programs/psxarc/tarhead.h
@@ -0,0 +1,28 @@
+#ifndef _TARHEAD_
+#define _TARHEAD_
+
+#define TNAMSIZ 100
+
+typedef union _TAR_HEAD {
+ unsigned char buf[512];
+ struct {
+ char name[TNAMSIZ]; /* nul-terminated unless full */
+ char mode[8];
+ char uid[8];
+ char gid[8];
+ char size[12];
+ char mtime[12];
+ char chksum[8];
+ char typeflag;
+ char linkname[TNAMSIZ]; /* nul-terminated unless full */
+ char magic[TMAGLEN]; /* nul-terminated */
+ char version[2];
+ char uname[32]; /* nul-terminated */
+ char gname[32]; /* nul-terminated */
+ char devmajor[8];
+ char devminor[8];
+ char prefix[155]; /* nul-terminated unless full */
+ } s;
+} TAR_HEAD, *PTAR_HEAD;
+
+#endif // _TARHEAD_
diff --git a/private/posix/programs/psxses/ansiio.h b/private/posix/programs/psxses/ansiio.h
new file mode 100644
index 000000000..27bf23850
--- /dev/null
+++ b/private/posix/programs/psxses/ansiio.h
@@ -0,0 +1,89 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ tty.h
+
+Abstract:
+
+ Prototypes for functions & macros in viotty.c
+
+Author:
+
+ Michael Jarus (mjarus) 28-Apr-1992
+
+Environment:
+
+ User Mode Only
+
+Revision History:
+
+--*/
+
+
+/*
+ * - character definitions for ANSI X3.64
+ */
+
+#define ANSI_ESC 0x1B /* ESC - escape */
+#define ANSI_CUU 0x41 /* ESC[<n>A - cursor up */
+#define ANSI_CUD 0x42 /* ESC[<n>B - cursor down */
+#define ANSI_CUF 0x43 /* ESC[<n>C - cursor forward */
+#define ANSI_CUB 0x44 /* ESC[<n>D - cursor back */
+#define ANSI_CUP 0x48 /* ESC[<row>;<col>H - cursor position */
+#define ANSI_ED 0x4A /* ESC[2J - erase display */
+#define ANSI_EL 0x4B /* ESC[K - erase line */
+#if 0
+// These don't seem to be part of ANSI X3.64. -mjb
+#define ANSI_SCP 0x53 /* ESC[S - save cursor position */
+#define ANSI_RCP 0x55 /* ESC[U - restore cursor position */
+#endif
+#define ANSI_CUP1 0x66 /* ESC[<row>;<col>f - cursor position */
+#define ANSI_SMOD 0x68 /* ESC[=<s>h - set mode */
+#define ANSI_RMOD 0x6C /* ESC[=<s>l - reset mode */
+#define ANSI_SGR 0x6D /* ESC[<g1>;...;<gn>m - select graphic rendition */
+#define ANSI_ICH 0x40 /* ESC[@ insert character */
+#define ANSI_CNL 0x45 /* ESC[E cursor to next line */
+#define ANSI_CPL 0x46 /* ESC[F cursor to previous line */
+#define ANSI_IL 0x4C /* ESC[L insert line */
+#define ANSI_DL 0x4D /* ESC[M delete line */
+#define ANSI_DCH 0x50 /* ESC[P delete character */
+#define ANSI_SU 0x53 /* ESC[S scroll up */
+#define ANSI_SD 0x54 /* ESC[T scroll down */
+#define ANSI_ECH 0x58 /* ESC[X erase character */
+#define ANSI_CBT 0x5A /* ESC[Z backward tabulation */
+
+/* states of the finite state machine */
+
+#define NOCMD 1 /* type of crt state - most chars will go onto screen */
+#define ESCED 2 /* we've seen an ESC, waiting for rest of CSI */
+#define PARAMS 3 /* we're building the parameter list */
+#define MODCMD 4 /* we've seen "ESC[=" waiting for #h or #l (# in {0..7}) */
+#define MODDBCS 5 /* we've seen DBCS lead-in char */
+
+#define NPARMS 3 /* max # of params */
+
+COORD ansi_coord; /* current active position */
+COORD ansi_scp; /* CurPos for saving */
+USHORT ansi_param[NPARMS]; /* parameter list */
+USHORT ansi_pnum; /* index of parameter we're building */
+USHORT ansi_state; /* state of machine */
+WORD ansi_base; /* base colors value */
+USHORT ansi_reverse; /* reverse flag */
+USHORT ignore_next_char;
+COORD TTYCoord;
+HANDLE TTYcs;
+LPSTR TTYDestStr;
+LPSTR TTYTextPtr;
+DWORD TTYNumBytes;
+BOOL TTYCtrlCharInStr;
+BOOL TTYOldCtrlCharInStr;
+
+VOID clrparam(void);
+DWORD ansicmd(IN HANDLE __cs, IN register CHAR __c);
+int range(register int __val, register int __def, int __min, int __max);
+DWORD SetTTYAttr(IN HANDLE __cs, IN USHORT __AnsiParm);
+DWORD TTYFlushStr(USHORT *__newcoord, const char *__call);
+
diff --git a/private/posix/programs/psxses/conreqst.c b/private/posix/programs/psxses/conreqst.c
new file mode 100644
index 000000000..654c5dd48
--- /dev/null
+++ b/private/posix/programs/psxses/conreqst.c
@@ -0,0 +1,159 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ conrqust.c
+
+Abstract:
+
+ This module contains the handler for console requests.
+
+Author:
+
+ Avi Nathan (avin) 17-Jul-1991
+
+Environment:
+
+ User Mode Only
+
+Revision History:
+
+ Ellen Aycock-Wright (ellena) 15-Sept-1991 Modified for POSIX
+
+--*/
+
+#define WIN32_ONLY
+#include "psxses.h"
+#include <stdio.h>
+#include <io.h>
+#include <fcntl.h>
+#include <errno.h>
+
+//
+// ServeConRequest -- perform a console request
+//
+// pReq - the request message
+// pStatus - returned errno, 0 for success
+//
+// returns: TRUE (reply to the caller)
+//
+
+BOOL
+ServeConRequest(
+ IN PSCCONREQUEST pReq,
+ OUT PDWORD pStatus
+ )
+{
+ DWORD IoLengthDone;
+ DWORD Rc;
+ HANDLE h;
+ int fd;
+ int error;
+
+ *pStatus = 0;
+
+ //
+ // If the KbdThread has not been started, we do IO in the
+ // conventional way. Since starting the thread depends on the
+ // user setting an environment variable, this is a backward-
+ // compatibility mode.
+ //
+
+ switch (pReq->Request) {
+ case ScReadFile:
+ fd = (int)pReq->d.IoBuf.Handle;
+
+ if (DoTrickyIO && _isatty(fd)) {
+
+ IoLengthDone = TermInput(hConsoleInput,
+ PsxSessionDataBase,
+ (unsigned int)pReq->d.IoBuf.Len,
+ pReq->d.IoBuf.Flags,
+ &error
+ );
+ } else {
+ _setmode(fd, _O_BINARY);
+
+ IoLengthDone = _read(fd,
+ PsxSessionDataBase,
+ (unsigned int)pReq->d.IoBuf.Len);
+ if (-1 == IoLengthDone) {
+ error = EBADF;
+ }
+ }
+
+ pReq->d.IoBuf.Len = IoLengthDone;
+
+ if (IoLengthDone == -1) {
+ *pStatus = error;
+ }
+ break;
+
+ case ScWriteFile:
+ fd = (int)pReq->d.IoBuf.Handle;
+
+ if (fd > 2) {
+ fd++;
+ }
+
+ if (DoTrickyIO && _isatty(fd)) {
+ IoLengthDone = TermOutput(hConsoleOutput,
+ (LPSTR)PsxSessionDataBase,
+ (DWORD)pReq->d.IoBuf.Len);
+ } else {
+ // not a tty.
+
+ _setmode(fd, _O_BINARY);
+
+ IoLengthDone = _write(fd, PsxSessionDataBase,
+ (unsigned int)pReq->d.IoBuf.Len);
+ }
+ pReq->d.IoBuf.Len = IoLengthDone;
+ if (-1 == IoLengthDone) {
+ *pStatus = EBADF;
+ }
+ break;
+
+ case ScKbdCharIn:
+ Rc = GetPsxChar(&pReq->d.AsciiChar);
+ break;
+
+ case ScIsatty:
+ if (!DoTrickyIO) {
+ // then it's never a tty.
+ pReq->d.IoBuf.Len = 0;
+ break;
+ }
+ pReq->d.IoBuf.Len = (_isatty((int)pReq->d.IoBuf.Handle) != 0);
+ break;
+
+ case ScIsatty2:
+
+ pReq->d.IoBuf.Len = (_isatty((int)pReq->d.IoBuf.Handle) != 0);
+ break;
+
+ case ScOpenFile:
+ fd = _open("CONIN$", _O_RDWR);
+ _open("CONOUT$", _O_RDWR);
+
+ pReq->d.IoBuf.Handle = (HANDLE)fd;
+ break;
+
+ case ScCloseFile:
+#if 0
+//
+// This code causes the keybd thread to fail ReadConsole() with
+// STATUS_INVALID_HANDLE.
+//
+ _close((int)pReq->d.IoBuf.Handle);
+#endif
+ break;
+
+ default:
+ *pStatus = EINVAL;
+ }
+
+ return TRUE;
+}
diff --git a/private/posix/programs/psxses/error.c b/private/posix/programs/psxses/error.c
new file mode 100644
index 000000000..57a99a2ba
--- /dev/null
+++ b/private/posix/programs/psxses/error.c
@@ -0,0 +1,35 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include <io.h>
+#include <windows.h>
+
+void
+error(
+ unsigned uMsgNum, // message number
+ ... // optional args
+ )
+{
+ unsigned len;
+ void *p;
+ va_list args;
+
+ va_start(args, uMsgNum);
+
+ len = FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE,
+ NULL, // addr of msg source
+ uMsgNum, // message number
+ 0, // lang id
+ (LPTSTR)&p, // pointer to buffer
+ 0, // min size to allocate
+ &args
+ );
+ va_end(args);
+ if (0 == len) {
+ return;
+ }
+ _write(_fileno(stderr), p, len);
+ LocalFree(p);
+
+ return;
+}
diff --git a/private/posix/programs/psxses/error.h b/private/posix/programs/psxses/error.h
new file mode 100644
index 000000000..2354cb724
--- /dev/null
+++ b/private/posix/programs/psxses/error.h
@@ -0,0 +1,3 @@
+
+
+extern void error(unsigned uMsgNum, ...);
diff --git a/private/posix/programs/psxses/errors.mc b/private/posix/programs/psxses/errors.mc
new file mode 100644
index 000000000..78e19e4ab
--- /dev/null
+++ b/private/posix/programs/psxses/errors.mc
@@ -0,0 +1,20 @@
+MessageId=1 SymbolicName=MSG_UNKNOWN_FLAG
+Language=English
+posix: unknown flag %1
+.
+MessageId= SymbolicName=MSG_USAGE
+Language=English
+usage: posix /c <path> [<args>]
+.
+MessageId= SymbolicName=MSG_COMMAND_MISSING
+Language=English
+posix: command missing
+.
+MessageId= SymbolicName=MSG_CANNOT_CONNECT
+Language=English
+posix: cannot connect to posix subsystem
+.
+MessageId= SymbolicName=MSG_CANNOT_START_PROC
+Language=English
+posix: cannot start process
+.
diff --git a/private/posix/programs/psxses/kbdutil.c b/private/posix/programs/psxses/kbdutil.c
new file mode 100644
index 000000000..f728a6c15
--- /dev/null
+++ b/private/posix/programs/psxses/kbdutil.c
@@ -0,0 +1,71 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ kbdutil.c
+
+Abstract:
+
+ This module contains the KBD utilities
+
+Author:
+
+ Avi Nathan (avin) 17-Jul-1991
+
+Environment:
+
+ User Mode Only
+
+Revision History:
+
+ Ellen Aycock-Wright (ellena) 15-Sept-1991 - Modified for Posix
+
+--*/
+
+#define WIN32_ONLY
+#include "psxses.h"
+
+
+DWORD
+GetPsxChar(
+ OUT PCHAR AsciiChar
+ )
+{
+
+ DWORD Rc;
+ INPUT_RECORD In;
+ DWORD cEvents;
+
+ for(;;) {
+ Rc = ReadConsoleInput(
+ hConsoleInput,
+ &In,
+ 1L,
+ &cEvents
+ );
+
+ if ( !Rc ) {
+ return(GetLastError());
+ }
+
+ if ( In.EventType != KEY_EVENT ) {
+ continue;
+ }
+ if ( !In.Event.KeyEvent.bKeyDown ) {
+ continue;
+ }
+
+ if ( !In.Event.KeyEvent.uChar.AsciiChar ) {
+ continue;
+ }
+
+ *AsciiChar = In.Event.KeyEvent.uChar.AsciiChar;
+
+ break;
+ }
+
+ return(0L);
+
+}
diff --git a/private/posix/programs/psxses/makefile b/private/posix/programs/psxses/makefile
new file mode 100644
index 000000000..afc6030de
--- /dev/null
+++ b/private/posix/programs/psxses/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT.
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/posix/programs/psxses/makefile.inc b/private/posix/programs/psxses/makefile.inc
new file mode 100644
index 000000000..97be7d3ea
--- /dev/null
+++ b/private/posix/programs/psxses/makefile.inc
@@ -0,0 +1,2 @@
+errors.h errors.rc msg00001.bin: errors.mc
+ mc -v errors.mc
diff --git a/private/posix/programs/psxses/ntinitss.c b/private/posix/programs/psxses/ntinitss.c
new file mode 100644
index 000000000..d6f1a43e0
--- /dev/null
+++ b/private/posix/programs/psxses/ntinitss.c
@@ -0,0 +1,1125 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ ntinitss.c
+
+Abstract:
+
+ This module contains the code to establish the connection between
+ the session console process and the PSX Emulation Subsystem.
+
+Author:
+
+ Avi Nathan (avin) 17-Jul-1991
+
+Environment:
+
+ User Mode Only
+
+Revision History:
+
+ Ellen Aycock-Wright (ellena) 15-Sept-1991 Modified for POSIX
+
+--*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <io.h>
+#define _POSIX_
+#include <limits.h>
+#define NTPSX_ONLY
+#include "psxses.h"
+#include <ntsm.h>
+#include "sesport.h"
+
+#define COPY_TO_SESSION_DATABASE(pchEnvp) { \
+ *ppch++ = TmpPtr - (ULONG)Buf; \
+ (void)strcpy(TmpPtr, pchEnvp); \
+ TmpPtr += strlen(TmpPtr); \
+ *TmpPtr++ = '\0'; } \
+
+#define MyFree(x) if(NULL!=x)free(x);
+
+const int iDaysInMonths[] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
+
+//
+// PSXSES protocol with PSXSS
+//
+// Connect
+// 1. PSXSES ----------> PSXSS
+//
+//
+// Connect
+// 2. PSXSES <---------- PSXSS
+//
+//
+// SesConCreate
+// 3. PSXSES ----------> PSXSS (StartProcess)
+//
+//
+// PSXSES connects to the PSXSS, then PSXSS connects back. Then PSXSES
+// sends the Create
+//
+//
+
+/*
+ * prototypes for internal functions.
+ */
+PCHAR BuildTZEnvVar();
+PCHAR ConvertTimeFromBias( LONG );
+int MakeJulianDate( PTIME_FIELDS );
+PCHAR MakeTime( PTIME_FIELDS );
+PCHAR MakeTZName( WCHAR * );
+BOOL CreateConsoleDataSection(VOID);
+PCHAR ConvertPathVar(PCHAR);
+
+int
+CountOpenFiles()
+/*++
+
+CountOpenFiles -- return the number of file descriptors in
+ use.
+
+--*/
+{
+ int i;
+
+ i = _dup(0);
+ if (-1 == i) {
+ if (EBADF == errno) {
+ return 0;
+ }
+ if (EMFILE == errno) {
+ return _NFILE;
+ }
+ // what other error?
+ return 3;
+ }
+ _close(i);
+ return i;
+}
+
+DWORD
+InitPsxSessionPort(
+ VOID
+ )
+{
+ char PortName[PSX_SES_BASE_PORT_NAME_LENGTH];
+ STRING PsxSessionPortName;
+ UNICODE_STRING PsxSessionPortName_U;
+ UNICODE_STRING PsxSSPortName;
+ HANDLE RequestThread;
+ DWORD dwThreadId;
+
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ ULONG ConnectionInfoLen;
+ PSXSESCONNECTINFO ConnectionInfo;
+ SECURITY_QUALITY_OF_SERVICE DynamicQos;
+ BOOLEAN DeferedPosixLoadAttempted = FALSE;
+
+ if (!CreateConsoleDataSection()) {
+ return(0);
+ }
+
+ //
+ // Get a private ID for the session port.
+ //
+ PSX_GET_SESSION_PORT_NAME((char *)&PortName, GetSessionUniqueId());
+ RtlInitAnsiString(&PsxSessionPortName, PortName);
+
+ //
+ // Create session port
+ //
+
+ Status = RtlAnsiStringToUnicodeString(&PsxSessionPortName_U,
+ &PsxSessionPortName, TRUE);
+ if (!NT_SUCCESS(Status)) {
+ PsxSessionPort = NULL;
+ return 0;
+ }
+
+ InitializeObjectAttributes(&ObjectAttributes, &PsxSessionPortName_U, 0,
+ NULL, NULL);
+ Status = NtCreatePort(&PsxSessionPort, &ObjectAttributes,
+ sizeof(SCCONNECTINFO), sizeof(SCREQUESTMSG),
+ 4096 * 16);
+
+ RtlFreeUnicodeString(&PsxSessionPortName_U);
+
+ if (!NT_SUCCESS(Status)) {
+ PsxSessionPort = NULL;
+ return 0;
+ }
+
+ //
+ // Create a thread to handle requests to the session port including
+ // connection requests.
+ //
+
+ RequestThread = CreateThread( NULL,
+ 0,
+ ServeSessionRequests,
+ NULL,
+ 0,
+ &dwThreadId
+ );
+ if (RequestThread == NULL) {
+ NtClose( PsxSessionPort );
+ return 0;
+ }
+
+ SetThreadPriority(RequestThread, THREAD_PRIORITY_ABOVE_NORMAL);
+
+ //
+ // connect to PSXSS and notify of the new session and the port associated
+ // with it. It will connected back to the session port just created.
+ //
+
+ ConnectionInfo.In.SessionUniqueId = GetSessionUniqueId();
+ ConnectionInfoLen = sizeof(ConnectionInfo);
+
+ RtlInitUnicodeString(&PsxSSPortName, PSX_SS_SESSION_PORT_NAME);
+
+ //
+ // Set up the security quality of service parameters to use over the
+ // port. Use the most efficient (least overhead) - which is dynamic
+ // rather than static tracking.
+ //
+
+ DynamicQos.ImpersonationLevel = SecurityImpersonation;
+ DynamicQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ DynamicQos.EffectiveOnly = TRUE;
+
+retry_connect:
+ Status = NtConnectPort(&PsxSSPortHandle, &PsxSSPortName, &DynamicQos,
+ NULL, NULL, NULL, (PVOID)&ConnectionInfo,
+ &ConnectionInfoLen);
+ if (!NT_SUCCESS(Status)) {
+
+ if ( DeferedPosixLoadAttempted == FALSE ) {
+ HANDLE SmPort;
+ UNICODE_STRING PosixName;
+
+ DeferedPosixLoadAttempted = TRUE;
+
+ Status = SmConnectToSm(NULL,NULL,0,&SmPort);
+ if ( NT_SUCCESS(Status) ) {
+ RtlInitUnicodeString(&PosixName,L"POSIX");
+ SmLoadDeferedSubsystem(SmPort,&PosixName);
+ goto retry_connect;
+ }
+ }
+
+ KdPrint(("PSXSES: Unable to connect to %ws: %X\n",
+ PsxSSPortName.Buffer, Status));
+ return 0;
+ }
+ return (DWORD)ConnectionInfo.Out.SessionPortHandle;
+}
+
+VOID
+ScHandleConnectionRequest(
+ IN PSCREQUESTMSG Message
+ )
+{
+ NTSTATUS Status;
+ PSCCONNECTINFO ConnectionInfo = &Message->ConnectionRequest;
+ HANDLE CommPortHandle;
+ PORT_VIEW ServerView;
+
+ // BUGBUG: Add verification test
+ if (FALSE) {
+ // Reject
+ Status = NtAcceptConnectPort(&CommPortHandle, NULL,
+ (PPORT_MESSAGE) Message, FALSE, NULL, NULL);
+ } else {
+ // ??? Any reply
+ ConnectionInfo->dummy = 0;
+
+ // BUGBUG:
+ ServerView.Length = sizeof(ServerView);
+ ServerView.SectionOffset = 0L;
+ ServerView.ViewSize = 0L;
+
+ Status = NtAcceptConnectPort(&CommPortHandle, NULL,
+ (PPORT_MESSAGE) Message, TRUE, NULL, NULL);
+
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSES: Accept failed: %X\n",
+ Status));
+ exit(1);
+ }
+ //
+ // Record the view section address in a
+ // global variable.
+ //
+ // BUGBUG: PsxSesConPortBaseAddress =
+ // ServerView.ViewBase;
+
+ Status = NtCompleteConnectPort(CommPortHandle);
+ ASSERT(NT_SUCCESS(Status));
+
+ {
+ PCLIENT_AND_PORT pc;
+
+ pc = malloc(sizeof(*pc));
+ if (NULL == pc) {
+ return;
+ }
+
+ pc->ClientId = Message->h.ClientId;
+ pc->CommPort = CommPortHandle;
+ InsertTailList(&ClientPortsList, &pc->Links);
+
+ }
+
+ }
+}
+
+//
+// create a section to be shared by all client processes running in this
+// session. returns a pointer to the base.
+//
+BOOL
+CreateConsoleDataSection(
+ VOID
+ )
+{
+ char SessionName[PSX_SES_BASE_PORT_NAME_LENGTH];
+ STRING PsxSessionDataName;
+ UNICODE_STRING PsxSessionDataName_U;
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ HANDLE SectionHandle;
+ LARGE_INTEGER SectionSize;
+ ULONG ViewSize=0L;
+ BOOLEAN DeferedPosixLoadAttempted = FALSE;
+
+ //
+ // Get a private ID for the session data.
+ //
+ PSX_GET_SESSION_DATA_NAME((char *)&SessionName, GetSessionUniqueId());
+ RtlInitAnsiString(&PsxSessionDataName, SessionName);
+ Status = RtlAnsiStringToUnicodeString(&PsxSessionDataName_U,
+ &PsxSessionDataName, TRUE);
+ ASSERT(NT_SUCCESS(Status));
+
+ InitializeObjectAttributes(&ObjectAttributes, &PsxSessionDataName_U, 0,
+ NULL, NULL);
+
+ //
+ // create a 64k section.
+ // BUGBUG: cruiser apis allow io of more then 64k
+ //
+
+retry_create:
+ SectionSize.LowPart = PSX_SESSION_PORT_MEMORY_SIZE;
+ SectionSize.HighPart = 0L;
+
+ Status = NtCreateSection(&SectionHandle,
+ SECTION_MAP_WRITE,
+ &ObjectAttributes, &SectionSize, PAGE_READWRITE,
+ SEC_COMMIT, NULL);
+
+ if (!NT_SUCCESS(Status)) {
+
+ if ( DeferedPosixLoadAttempted == FALSE ) {
+ HANDLE SmPort;
+ UNICODE_STRING PosixName;
+ LARGE_INTEGER TimeOut;
+
+ DeferedPosixLoadAttempted = TRUE;
+
+ Status = SmConnectToSm(NULL,NULL,0,&SmPort);
+ if ( NT_SUCCESS(Status) ) {
+ RtlInitUnicodeString(&PosixName,L"POSIX");
+ SmLoadDeferedSubsystem(SmPort,&PosixName);
+
+ //
+ // Sleep for 1 seconds
+ //
+
+ TimeOut.QuadPart = (LONGLONG)1000 * (LONGLONG)-10000;
+ NtDelayExecution(FALSE,&TimeOut);
+ goto retry_create;
+ }
+ }
+
+ RtlFreeUnicodeString(&PsxSessionDataName_U);
+ KdPrint(("PSXSES: NtCreateSection (%wZ) failed: 0x%x\n",
+ &PsxSessionDataName_U, Status));
+ return FALSE;
+ }
+
+ RtlFreeUnicodeString(&PsxSessionDataName_U);
+
+ //
+ // Map the the whole section to virtual address.
+ // Let MM locate the view.
+ //
+
+ PsxSessionDataBase = 0L;
+ Status = NtMapViewOfSection(SectionHandle, NtCurrentProcess(),
+ &PsxSessionDataBase, 0L, 0L, NULL,
+ &ViewSize, ViewUnmap, 0L, PAGE_READWRITE);
+
+ if (!NT_SUCCESS(Status)) {
+ ASSERT(NT_SUCCESS(Status));
+ return FALSE;
+ }
+
+ PsxSessionDataSectionHandle = SectionHandle;
+ return(TRUE);
+}
+
+BOOL
+StartProcess(
+ DWORD SessionPortHandle,
+ char *PgmName,
+ char *CurrentDir,
+ int argc,
+ char **argv,
+ char **envp
+ )
+/*++
+
+Description:
+
+ Start the POSIX process running by calling PsxSesConCreate. We use
+ the port memory to pass the name of the command, its args, and the
+ environment strings. The layout of the port memory looks like this:
+
+ PsxSessionDataBase:
+ Command line, nul-terminated.
+ Buff:
+ argv[0]
+ argv[1]
+ ...
+ NULL
+ environ[0]
+ environ[1]
+ ...
+ NULL
+ <argv strings>
+ <environ strings>
+
+ Since we'll be passing this to the server process, we make all the
+ pointers relative to Buff.
+
+--*/
+{
+ PSXSESREQUESTMSG RequestMsg;
+ PSXSESREQUESTMSG ReplyMsg;
+ PSCREQ_CREATE Create;
+ NTSTATUS Status;
+ static char path[256];
+ char *Buf;
+ char *TmpPtr;
+ int i;
+ char **ppch;
+ char *pch;
+ char *pchHomeDrive; // pointer to HOMEDRIVE environ var
+ char *pchHomePath; // pointer to HOMEPATH environ var
+ BOOLEAN fSuccess;
+
+ UNICODE_STRING DosPath_U, Path_U;
+ ANSI_STRING DosPath_A, Path_A;
+
+ //
+ // Set Header info
+ //
+
+ PORT_MSG_DATA_LENGTH(RequestMsg) = sizeof(RequestMsg) -
+ sizeof(PORT_MESSAGE);
+ // BUGBUG: too much
+ PORT_MSG_TOTAL_LENGTH(RequestMsg) = sizeof(RequestMsg);
+ PORT_MSG_ZERO_INIT(RequestMsg) = 0L;
+
+ //
+ // Set request info
+ //
+
+ // BUGBUG: use common memory
+ RequestMsg.Request = SesConCreate;
+ Create = &RequestMsg.d.Create;
+
+ Create->SessionUniqueId = GetSessionUniqueId();
+ Create->SessionPort = (HANDLE)SessionPortHandle;
+ Create->Buffer = PsxSessionDataBase;
+ Create->OpenFiles = CountOpenFiles();
+
+ Buf = PsxSessionDataBase;
+ Create->PgmNameOffset = 0;
+
+ RtlInitAnsiString(&DosPath_A, PgmName);
+
+ Status = RtlAnsiStringToUnicodeString(&DosPath_U, &DosPath_A, TRUE);
+
+ fSuccess = RtlDosPathNameToNtPathName_U(
+ DosPath_U.Buffer,
+ &Path_U,
+ NULL,
+ NULL
+ );
+
+ RtlFreeUnicodeString(&DosPath_U);
+ if (!fSuccess) {
+ return fSuccess;
+ }
+
+ RtlUnicodeStringToAnsiString(&Path_A, &Path_U, TRUE);
+
+ strcpy(Buf, Path_A.Buffer);
+
+ RtlFreeAnsiString(&Path_A);
+ RtlFreeHeap(RtlProcessHeap(), 0, (PVOID)Path_U.Buffer);
+
+ Buf += strlen(Buf) + 1;
+
+ if ((ULONG)Buf & 0x1)
+ ++Buf;
+ if ((ULONG)Buf & 0x2)
+ Buf += 2;
+
+ Create->CurrentDirOffset = (ULONG)(Buf - (ULONG)PsxSessionDataBase);
+
+ strcpy(Buf, CurrentDir);
+ Buf += strlen(Buf) + 1;
+
+ if ((ULONG)Buf & 0x1)
+ ++Buf;
+ if ((ULONG)Buf & 0x2)
+ Buf += 2;
+
+ Create->ArgsOffset = (ULONG)(Buf - (ULONG)PsxSessionDataBase);
+
+ //
+ // We need to know how much space to leave for the argv and environ
+ // vectors to know where to start the strings. We have argc, which
+ // was passed in, but we need to count the environ strings.
+ //
+
+ argc++; // for trailing argv null
+ for (ppch = envp; NULL != *ppch; ++ppch)
+ ++argc;
+
+ ++argc; // for trailing environ null
+ ++argc; // for LOGNAME
+ ++argc; // for HOME
+ ++argc; // for TZ
+ ++argc; // for _PSXLIBPATH
+
+argc += 1;
+
+ //
+ // TmpPtr indicates the place at which the argument and environment
+ // strings will be placed.
+ //
+
+ TmpPtr = &Buf[argc * sizeof(char *)];
+
+ // copy the argv pointers and strings
+
+ ppch = (char **)&Buf[0];
+ while (NULL != *argv) {
+ *ppch++ = TmpPtr - (ULONG)Buf;
+ (void)strcpy(TmpPtr, *argv++);
+
+ TmpPtr += strlen(TmpPtr);
+ *TmpPtr++ = '\0';
+ }
+ *ppch++ = NULL;
+
+ Create->EnvironmentOffset = (ULONG)TmpPtr - (ULONG)PsxSessionDataBase;
+
+ pchHomeDrive = NULL;
+ pchHomePath = NULL;
+
+ while (NULL != *envp) {
+ char *p;
+ int bPathFound = 0;
+
+ //
+ // Upcase the variable part of each environment string.
+ //
+
+ if (NULL == (p = strchr(*envp, '='))) {
+ // no equals sign? Skip this one.
+ *envp++;
+ continue;
+ }
+ *p = '\0';
+ _strupr(*envp);
+ if (0 == strcmp(*envp, "PATH") && !bPathFound) {
+ bPathFound = 1;
+ // Save PATH as LIBPATH.
+
+ pch = malloc(strlen(p + 1) + sizeof("_PSXLIBPATH") + 2);
+ if (NULL != pch) {
+ strcpy(pch, "_PSXLIBPATH=");
+ strcat(pch, p + 1);
+ COPY_TO_SESSION_DATABASE(pch);
+ free(pch);
+ }
+
+ // Convert PATH to POSIX-format
+ *p = '=';
+ *envp = ConvertPathVar(*envp);
+ if (NULL == *envp) {
+ // no memory; skip the path
+ *envp++;
+ continue;
+ }
+ } else if (0 == strcmp(*envp, "USERNAME")) {
+ // Copy USERNAME to LOGNAME
+ *p = '=';
+ pch = malloc(strlen(*envp));
+ if (NULL == pch) {
+ continue;
+ }
+ sprintf(pch,"LOGNAME=%s",strchr(*envp,'=')+1);
+ COPY_TO_SESSION_DATABASE(pch);
+ free(pch);
+ } else if (0 == strcmp(*envp, "HOMEPATH")) {
+ pchHomePath = p+1;
+ *p = '=';
+ } else if (0 == strcmp(*envp, "HOMEDRIVE")) {
+ pchHomeDrive = p+1;
+ *p = '=';
+ } else {
+ *p = '=';
+ }
+ COPY_TO_SESSION_DATABASE(*envp);
+ *envp++;
+ }
+
+ //
+ // setup the TZ env var
+ //
+
+ pch = BuildTZEnvVar();
+ if (NULL != pch) {
+ COPY_TO_SESSION_DATABASE(pch);
+ free(pch);
+ }
+
+ //
+ // if the HOMEPATH env var was not set, we'll have to set HOME to //C/
+ //
+
+ if (NULL == pchHomePath || NULL == pchHomeDrive) {
+ COPY_TO_SESSION_DATABASE("HOME=//C/");
+ } else {
+ char *pch2;
+ pch = malloc((5 + strlen(pchHomeDrive)+strlen(pchHomePath))*2);
+ if (NULL != pch) {
+ sprintf(pch,"HOME=%s%s",pchHomeDrive,pchHomePath);
+ pch2 = ConvertPathVar(pch);
+ free(pch);
+ if (NULL != pch2) {
+ COPY_TO_SESSION_DATABASE(pch2);
+ }
+ }
+ }
+
+*ppch = NULL;
+
+ //
+ // for all request pass the session handle except for the CREATE request
+ // where no session have been allocated yet.
+ //
+
+ RequestMsg.Session = NULL;
+
+ Status = NtRequestWaitReplyPort(PsxSSPortHandle, (PPORT_MESSAGE)&RequestMsg,
+ (PPORT_MESSAGE)&ReplyMsg);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSES: Unable to exec process: %X\n", Status));
+ return FALSE;
+ }
+
+ ASSERT(PORT_MSG_TYPE(ReplyMsg) == LPC_REPLY);
+
+ //
+ // get the session handle for the newly created session.
+ //
+ SSSessionHandle = ReplyMsg.d.Create.SessionPort;
+
+ return(NT_SUCCESS(ReplyMsg.Status));
+}
+
+VOID
+TerminateSession(
+ IN ULONG ExitStatus
+ )
+{
+ NTSTATUS Status;
+
+ //
+ // remove event handler so we don't try to send a message
+ // for a dying session.
+ //
+ SetEventHandlers(FALSE);
+
+ // Close the named objects: port, section
+
+ Status = NtClose(PsxSSPortHandle);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = NtClose(PsxSessionDataSectionHandle);
+ ASSERT(NT_SUCCESS(Status));
+
+ // notify PSXSS
+
+ // Cleanup TaskMan stuff
+
+ _exit(ExitStatus);
+}
+
+#define CTRL_CLOSE_EVENT 2
+#define CTRL_LOGOFF_EVENT 5
+#define CTRL_SHUTDOWN_EVENT 6
+
+BOOL
+EventHandlerRoutine(
+ IN DWORD CtrlType
+ )
+{
+ int SignalType;
+ BOOL r;
+
+#if 0
+//
+// These events are now handled -mjb.
+//
+ if (CTRL_CLOSE_EVENT == CtrlType) {
+ return FALSE; // not handled
+ }
+ if (CTRL_LOGOFF_EVENT == CtrlType) {
+ return FALSE; // not handled
+ }
+ if (CTRL_SHUTDOWN_EVENT == CtrlType) {
+ return FALSE; // not handled
+ }
+#endif
+
+ switch (CtrlType) {
+ case CTRL_C_EVENT:
+ r = TRUE;
+ SignalType = PSX_SIGINT;
+ break;
+
+ case CTRL_BREAK_EVENT:
+ r = TRUE;
+ SignalType = PSX_SIGQUIT;
+ break;
+ default:
+ r = FALSE;
+ SignalType = PSX_SIGKILL;
+ break;
+ }
+
+ SignalSession(SignalType);
+
+ //
+ // return TRUE to avoid having the default handler called and
+ // the process exited for us (the signal may be ignored or handled,
+ // after all).
+ //
+ // return FALSE to be exited immediately.
+ //
+
+ return r;
+}
+
+PCHAR
+ConvertPathVar(PCHAR opath)
+/*++
+ pch is malloced with twice the size of opath because the new path is
+ going to be longer than opath because of drive letter conversions
+ but will ALWAYS be less than strlen(opath)*2 chars.
+--*/
+{
+ char *pch, *p, *q;
+
+ pch = malloc(strlen(opath) * 2);
+ if (NULL == pch) {
+ return NULL;
+ }
+
+ p = pch;
+ while ('\0' != *opath) {
+ if (';' == *opath) {
+ // semis become colons
+ *p++ = ':';
+ } else if ('\\' == *opath) {
+ // back-slashes become slashes
+ *p++ = '/';
+ } else if (':' == *(opath + 1)) {
+ // "X:" becomes "//X"
+ *p++ = '/';
+ *p++ = '/';
+
+ // the drive letters must be uppercase.
+
+ *p++ = toupper(*opath);
+
+ ++opath; // skip the colon
+ } else {
+ *p++ = *opath;
+ }
+ ++opath;
+ }
+ *p = '\0';
+
+ return pch;
+}
+
+PCHAR
+BuildTZEnvVar(
+ )
+/*++
+
+Routine Description:
+
+ This routine allocates a buffer and formats the TZ environment
+ variable for Posix applications. The returned buffer is of the
+ form:
+
+ TZ=stdoffset[dst[offset][,start[/time],end[/time]]]
+
+ See IEEE Std 1003.1 page 152 for more information.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Pointer to a buffer containing the formatted TZ variable.
+
+--*/
+{
+
+ RTL_TIME_ZONE_INFORMATION TZInfo;
+ NTSTATUS Status;
+ int iTmp;
+ PCHAR pcRet;
+ PCHAR pcOffStr1, pcOffStr2;
+ PCHAR pcTimeStr1, pcTimeStr2;
+ PCHAR pcStandardName, pcDaylightName;
+ BOOL fDstSpecified;
+
+ pcOffStr1 = pcOffStr2 = NULL;
+ pcTimeStr1 = pcTimeStr2 = NULL;
+ pcStandardName = pcDaylightName = NULL;
+
+ pcRet = malloc( 60 + 2 * TZNAME_MAX ); // Conservative guess of max
+ if( NULL == pcRet ) {
+ return( NULL );
+ }
+
+ Status = RtlQueryTimeZoneInformation( &TZInfo );
+ if( !( NT_SUCCESS( Status ) ) ) {
+ return( NULL );
+ }
+
+ pcStandardName = MakeTZName( TZInfo.StandardName );
+ if (NULL == pcStandardName)
+ goto out;
+ pcOffStr1 = ConvertTimeFromBias( TZInfo.Bias + TZInfo.StandardBias );
+ if (NULL == pcOffStr1)
+ goto out;
+
+ //
+ // If DaylightStart.Month is 0, the date is not specified.
+ //
+
+ if (TZInfo.DaylightStart.Month != 0) {
+ pcDaylightName = MakeTZName( TZInfo.DaylightName );
+ pcTimeStr1 = MakeTime( &TZInfo.DaylightStart );
+ if (NULL == pcTimeStr1)
+ goto out;
+ pcTimeStr2 = MakeTime( &TZInfo.StandardStart );
+ if (NULL == pcTimeStr2)
+ goto out;
+ pcOffStr2 = ConvertTimeFromBias( TZInfo.Bias + TZInfo.DaylightBias );
+ if (NULL == pcOffStr2)
+ goto out;
+ fDstSpecified = TRUE;
+ } else {
+ fDstSpecified = FALSE;
+ }
+
+ if (fDstSpecified) {
+ if (TZInfo.DaylightStart.Year == 0) {
+ sprintf(pcRet, "TZ=%s%s%s%s,M%d.%d.%d/%s,M%d.%d.%d/%s",
+ pcStandardName, pcOffStr1,
+ pcDaylightName, pcOffStr2,
+ TZInfo.DaylightStart.Month,
+ TZInfo.DaylightStart.Day,
+ TZInfo.DaylightStart.Weekday,
+ pcTimeStr1,
+ TZInfo.StandardStart.Month,
+ TZInfo.StandardStart.Day,
+ TZInfo.StandardStart.Weekday,
+ pcTimeStr2);
+ } else {
+ sprintf(pcRet, "TZ=%s%s%s%s,J%d/%s,J%d/%s",
+ pcStandardName, pcOffStr1,
+ pcDaylightName, pcOffStr2,
+ MakeJulianDate(&TZInfo.DaylightStart),
+ pcTimeStr1,
+ MakeJulianDate(&TZInfo.StandardStart),
+ pcTimeStr2);
+ }
+ } else {
+ sprintf(pcRet, "TZ=%s%s", pcStandardName, pcOffStr1);
+ }
+
+out:
+ MyFree(pcOffStr1);
+ MyFree(pcOffStr2);
+ MyFree(pcTimeStr1);
+ MyFree(pcTimeStr2);
+ MyFree(pcStandardName);
+ MyFree(pcDaylightName);
+
+ return pcRet;
+
+}
+
+PCHAR
+MakeTZName(
+ IN WCHAR * FullName
+ )
+/*++
+
+Routine Description:
+
+ This routine formats a time zone name string from a full
+ name description of a time zone. The return string is the
+ first letter of each word in the input string, or TZNAME_MAX
+ chars from the full name if there are fewer than three words.
+ The returned buffer should be freed by the caller.
+
+Arguments:
+
+ FullName - pointer to the full name description of a time zone
+
+Return Value:
+
+ Pointer to a buffer containing the formatted time zone name.
+
+--*/
+{
+
+ PWCHAR Rover;
+ PCHAR cRet;
+ PCHAR DestRover;
+ BOOL GrabNext;
+ int GrabCount;
+
+ GrabNext = TRUE;
+ GrabCount = 0;
+ cRet = malloc( TZNAME_MAX+1 );
+ if( NULL == cRet ) {
+ return( NULL );
+ }
+ DestRover = cRet;
+
+ for( Rover = FullName; *Rover != L'\0'; Rover++ ) {
+ if( GrabNext ) {
+ if( *Rover == L' ' ) {
+ continue;
+ }
+ wctomb( DestRover, *Rover );
+ *DestRover++ = toupper( *DestRover );
+ if( ( ++GrabCount ) == TZNAME_MAX ) {
+ break;
+ }
+ GrabNext = FALSE;
+ } else {
+ if( *Rover == L' ' ) {
+ GrabNext = TRUE;
+ }
+ }
+ }
+
+ if( GrabCount < 3 ) {
+ wcstombs( cRet, FullName, TZNAME_MAX );
+ cRet[ TZNAME_MAX ] = '\0';
+ } else {
+ *DestRover = '\0';
+ }
+
+ return( cRet );
+
+}
+
+int
+MakeJulianDate(
+ IN PTIME_FIELDS tm
+ )
+/*++
+
+Routine Description:
+
+ This routine calculates Julian day n (1<=n<=365). Leap years are
+ not counted so Feb 29 is not representable.
+
+Arguments:
+
+ x - pointer to TIME_FIELDS structure to use as the input date
+
+Return Value:
+
+ Julian day.
+
+--*/
+{
+
+ int Idx;
+ int iJDate;
+
+ for( Idx = 0, iJDate = tm->Day;
+ Idx < tm->Month - 1; ++Idx ) {
+ iJDate += iDaysInMonths[Idx];
+ }
+ if( tm->Month == 2 && tm->Day == 29 ) {
+ --iJDate;
+ }
+ return( iJDate );
+
+}
+
+PCHAR
+ConvertTimeFromBias(
+ IN LONG Bias
+ )
+/*++
+
+Routine Description:
+
+ This routine allocates a buffer, and formats a time string in the
+ buffer as hh:mm:ss or hh:mm or hh:ss where hh may be only 1 digit.
+ The time may be negative. The buffer should be freed by the caller.
+
+Arguments:
+
+ x - pointer to a LONG representing the number of minutes of time
+
+Return Value:
+
+ Pointer to a buffer with the time string.
+
+--*/
+{
+
+ PCHAR cRet;
+ int iHours, iMins, iSecs;
+
+ cRet = malloc( 21 );
+ if( NULL == cRet ) {
+ return( NULL );
+ }
+ iHours = Bias / 60;
+ iMins = iSecs = 0;
+ if( Bias % 60 != 0 ) {
+ iMins = Bias / ( 60 * 60 );
+ if( Bias % ( 60 * 60 ) != 0 ) {
+ iSecs = Bias / ( 60 * 60 * 60 );
+ }
+ }
+
+ if( iSecs != 0 ) {
+ sprintf( cRet, "%d:%02d:%02d", iHours, iMins, iSecs );
+ } else if( iMins != 0 ) {
+ sprintf( cRet, "%d:%02d", iHours, iMins );
+ } else {
+ sprintf( cRet, "%d", iHours );
+ }
+ return( cRet );
+}
+
+PCHAR
+MakeTime(
+ IN PTIME_FIELDS x
+ )
+/*++
+
+Routine Description:
+
+ This routine allocates a buffer, and formats a time string in the
+ buffer as hh:mm:ss or hh:mm or hh:ss where hh may be only 1 digit.
+ The buffer should be freed by the caller.
+
+Arguments:
+
+ x - pointer to TIME_FIELDS structure to use as the input time
+
+Return Value:
+
+ Pointer to a buffer with the time string.
+
+--*/
+{
+
+ PCHAR cRet;
+
+ cRet = malloc( 21 );
+ if( NULL == cRet ) {
+ return( NULL );
+ }
+ if( x->Second != 0 ) {
+ sprintf( cRet, "%d:%02d:%02d", x->Hour, x->Minute, x->Second );
+ } else if( x->Minute != 0 ) {
+ sprintf( cRet, "%d:%02d", x->Hour, x->Minute );
+ } else {
+ sprintf( cRet, "%d", x->Hour );
+ }
+ return( cRet );
+}
+
+void
+SignalSession(
+ int SignalType
+ )
+{
+ PSXSESREQUESTMSG RequestMsg;
+ PSXSESREQUESTMSG ReplyMsg;
+ NTSTATUS Status;
+
+ //
+ // Set Header info
+ //
+
+ PORT_MSG_DATA_LENGTH(RequestMsg) = sizeof(RequestMsg) -
+ sizeof(PORT_MESSAGE);
+ PORT_MSG_TOTAL_LENGTH(RequestMsg) = sizeof(RequestMsg);
+ // BUGBUG: too much
+ PORT_MSG_ZERO_INIT(RequestMsg) = 0L;
+
+ RequestMsg.Request = SesConSignal;
+ RequestMsg.Session = SSSessionHandle;
+ RequestMsg.UniqueId = GetSessionUniqueId();
+ RequestMsg.d.Signal.Type = SignalType;
+
+ Status = NtRequestWaitReplyPort(PsxSSPortHandle,
+ (PPORT_MESSAGE)&RequestMsg, (PPORT_MESSAGE)&ReplyMsg);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSES: Unable to send signal: %X\n", Status));
+ ExitProcess(1);
+ }
+ if (LPC_REPLY != PORT_MSG_TYPE(ReplyMsg)) {
+ KdPrint(("PSXSES: unexpected MSG_TYPE %d\n", PORT_MSG_TYPE(ReplyMsg)));
+ ExitProcess(1);
+ }
+}
diff --git a/private/posix/programs/psxses/ntreqst.c b/private/posix/programs/psxses/ntreqst.c
new file mode 100644
index 000000000..e1f917d83
--- /dev/null
+++ b/private/posix/programs/psxses/ntreqst.c
@@ -0,0 +1,150 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ ntrqust.c
+
+Abstract:
+
+ This module contains the session requests thread.
+
+Author:
+
+ Avi Nathan (avin) 17-Jul-1991
+
+Environment:
+
+ User Mode Only
+
+Revision History:
+
+ Ellen Aycock-Wright (ellena) 15-Sept-1991 Modified for POSIX
+
+--*/
+
+#include <malloc.h>
+#define NTPSX_ONLY
+#include "psxses.h"
+
+VOID ScHandleConnectionRequest( PSCREQUESTMSG Message );
+
+DWORD
+WINAPI
+ServeSessionRequests(
+ LPVOID Parameter
+ )
+{
+
+ SCREQUESTMSG ReceiveMsg, *pReplyMsg;
+ NTSTATUS Status;
+ BOOL fCont = TRUE;
+
+ pReplyMsg = NULL;
+
+ InitializeListHead(&ClientPortsList);
+
+ for (;;) {
+ if (fCont) {
+ Status = NtReplyWaitReceivePort(PsxSessionPort,
+ NULL, (PPORT_MESSAGE)pReplyMsg,
+ (PPORT_MESSAGE)&ReceiveMsg);
+ if (ReceiveMsg.h.u2.s2.Type == LPC_CONNECTION_REQUEST) {
+ ASSERT(NT_SUCCESS(Status));
+ ScHandleConnectionRequest( &ReceiveMsg );
+ pReplyMsg = NULL;
+ continue;
+ }
+
+ } else {
+ Status = NtReplyPort(PsxSessionPort,
+ (PPORT_MESSAGE)pReplyMsg);
+ }
+
+ if (STATUS_INVALID_CID == Status) {
+ //
+ // The client on whose behalf we called read has
+ // been shot.
+ //
+
+ pReplyMsg = NULL;
+ continue;
+
+ }
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSES: ntreqst: 0x%x\n", Status));
+ }
+ ASSERT(NT_SUCCESS(Status));
+
+ if (LPC_PORT_CLOSED == PORT_MSG_TYPE(ReceiveMsg)) {
+ PLIST_ENTRY pl;
+ PCLIENT_AND_PORT pc;
+
+ for (pl = ClientPortsList.Flink;
+ pl != &ClientPortsList;
+ pl = pl->Flink) {
+
+ pc = (PVOID)pl;
+
+ if (pc->ClientId.UniqueProcess == ReceiveMsg.h.ClientId.UniqueProcess &&
+ pc->ClientId.UniqueThread == ReceiveMsg.h.ClientId.UniqueThread) {
+ RemoveEntryList(&pc->Links);
+ NtClose(pc->CommPort);
+ free(pc);
+ break;
+
+ }
+
+ }
+
+ pReplyMsg = NULL;
+ continue;
+ }
+
+ if (PORT_MSG_TYPE(ReceiveMsg) == LPC_CLIENT_DIED) {
+ pReplyMsg = NULL;
+ continue;
+ }
+
+ if (!fCont) {
+ break;
+ }
+
+ if (PORT_MSG_TYPE(ReceiveMsg) != LPC_REQUEST &&
+ PORT_MSG_TYPE(ReceiveMsg) != LPC_DATAGRAM) {
+ KdPrint(("PSXSES: got msg type %d\n",
+ PORT_MSG_TYPE(ReceiveMsg)));
+ }
+
+ try {
+ switch (ReceiveMsg.Request) {
+ case ConRequest:
+ fCont = ServeConRequest(&ReceiveMsg.d.Con,
+ &ReceiveMsg.Status);
+ break;
+ case TaskManRequest:
+ fCont = ServeTmRequest(&ReceiveMsg.d.Tm,
+ &ReceiveMsg.Status);
+ break;
+ case TcRequest:
+ fCont = ServeTcRequest(&ReceiveMsg.d.Tc,
+ &ReceiveMsg.Status);
+ break;
+ default:
+ KdPrint(("PSXSES: Unknown nt request: 0x%x\n",
+ ReceiveMsg.Request));
+ }
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ // BUGBUG! GetExceptionCode;
+
+ ReceiveMsg.Status = STATUS_ACCESS_VIOLATION;
+ // BUGBUG! The client should kill the process
+ }
+ pReplyMsg = &ReceiveMsg;
+ }
+
+ ExitProcess(0);
+ //NOTREACHED
+ return 0;
+}
diff --git a/private/posix/programs/psxses/posix.c b/private/posix/programs/psxses/posix.c
new file mode 100644
index 000000000..ae992252f
--- /dev/null
+++ b/private/posix/programs/psxses/posix.c
@@ -0,0 +1,314 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ posix.c
+
+Abstract:
+
+ This module contains the main of the session console process (posix.exe).
+
+Author:
+
+ Avi Nathan (avin) 17-Jul-1991
+
+Environment:
+
+ User Mode Only
+
+Revision History:
+
+ Ellen Aycock-Wright (ellena) 15-Sept-1991 - Modified for Posix
+
+--*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "error.h"
+#include "errors.h"
+#include "posixres.h"
+#define WIN32_ONLY
+#include "psxses.h"
+
+int
+GetCWD(
+ size_t size,
+ char *CurrentDir
+ );
+
+#define SKIP_ARG {argc--; argv++;}
+
+DWORD ServeKbdInput(LPVOID Parm);
+
+CRITICAL_SECTION KbdBufMutex;
+HANDLE hIoEvent;
+HANDLE hCanonEvent;
+BOOLEAN DoTrickyIO = FALSE;
+
+CRITICAL_SECTION StopMutex; // these for VSTOP/VSTART
+BOOLEAN bStop = FALSE;
+HANDLE hStopEvent;
+
+//
+// These are resources for "on" and "off", used throughout.
+//
+LPTSTR szOn, szOff;
+
+void
+_CRTAPI1
+main(
+ int argc,
+ char *argv[],
+ char *envp[]
+ )
+{
+ static char PgmFullPathBuf[MAX_PATH + 1];
+ static char CurrentDir[MAX_PATH + 1];
+ DWORD SessionPortHandle;
+ char *lpPgmName = NULL;
+ char *pch;
+ LPSTR lpFilePart;
+ int i;
+
+ DWORD dwThreadId;
+ HANDLE hThread;
+
+#if DBG
+ fTrace = FALSE;
+#endif
+
+ //
+ // skip program name
+ //
+ SKIP_ARG;
+
+ //
+ // look for flags for posix up to /C
+ //
+ //
+
+ while (argc) {
+ if ((argv[0][0] == '/') && ((argv[0][1]|('a'-'A')) == 'c')) {
+ if (argv[0][2]) {
+ argv[0] += 2;
+ } else {
+ SKIP_ARG;
+ }
+ break;
+ } else {
+ if (argv[0][0] == '/') {
+ switch (argv[0][1]|('a'-'A')) {
+ case 'p':
+ SKIP_ARG;
+ lpPgmName = *argv;
+ break;
+#if DBG
+ case 'b':
+#if 0
+ _asm int 3;
+#endif
+ break;
+ case 'v':
+ fVerbose = TRUE;
+ break;
+ case 't':
+ fTrace = TRUE;
+ break;
+#endif
+ default:
+ // error("posix: unknown flag: %s\n", argv[0]);
+ error(MSG_UNKNOWN_FLAG, argv[0]);
+ exit(1);
+ }
+ } else {
+ // error("usage: posix /c <path> [<args>]\n");
+ error(MSG_USAGE);
+ exit(1);
+ }
+ }
+ SKIP_ARG;
+ }
+
+ if (!argc) {
+ // error("posix: command missing\n");
+ error(MSG_COMMAND_MISSING);
+ exit(1);
+ }
+
+ //
+ // Set event handlers to handle Ctrl-C etc.
+ //
+
+ SetEventHandlers(TRUE);
+
+ //
+ // Connect with PSXSS
+ //
+
+ if (!(SessionPortHandle = InitPsxSessionPort()) ) {
+ // printf("posix: Cannot connect to Posix SubSystem\n");
+ error(MSG_CANNOT_CONNECT);
+ exit(1);
+ }
+
+ szOn = MyLoadString(IDS_ON);
+ szOff = MyLoadString(IDS_OFF);
+
+ pch = getenv("_POSIX_TERM");
+ if (NULL != pch && 0 == lstrcmpi(pch, szOn)) {
+ DoTrickyIO = TRUE;
+ }
+
+ if (DoTrickyIO) {
+ //
+ // Get handles to CONIN$ and CONOUT$ for terminal io.
+ //
+ //
+
+ hConsoleInput = CreateFile(
+ TEXT("CONIN$"),
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL
+ );
+ if (INVALID_HANDLE_VALUE == hConsoleInput) {
+ KdPrint(("POSIX: get con handle: 0x%x\n",
+ GetLastError()));
+ }
+
+ hConsoleOutput = CreateFile(
+ TEXT("CONOUT$"),
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL
+ );
+ if (INVALID_HANDLE_VALUE == hConsoleOutput) {
+ KdPrint(("POSIX: get con handle: 0x%x\n",
+ GetLastError()));
+ }
+
+ //
+ // Init terminal emulation globals.
+ //
+ TermioInit();
+ }
+
+ //
+ // get the full path of the program to execute
+ //
+
+ if (!lpPgmName) {
+ lpPgmName = argv[0];
+ }
+
+ GetFullPathName(lpPgmName, MAX_PATH, PgmFullPathBuf, &lpFilePart);
+
+ //
+ // Get our current working directory, so the Posix subsystem will know
+ // where to put the new Posix process.
+ //
+
+ (void)GetCWD(sizeof(CurrentDir), CurrentDir);
+
+ //
+ // Submit the request to start the process
+ //
+
+ if (!StartProcess(SessionPortHandle, PgmFullPathBuf, CurrentDir, argc,
+ argv, envp)) {
+ // printf("posix: Cannot start process\n");
+ error(MSG_CANNOT_START_PROC);
+ exit(1);
+ }
+
+ if (!DoTrickyIO) {
+ ExitThread(0);
+ }
+
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
+
+ InitializeCriticalSection(&KbdBufMutex);
+ hIoEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ hCanonEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ InitializeCriticalSection(&StopMutex);
+ hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ hThread = CreateThread(NULL, 0, ServeKbdInput, NULL, 0, &dwThreadId);
+
+ if (/* !hThread || */ !hIoEvent) {
+ KdPrint(("PSXSES: Cannot start keyboard server\n"));
+ exit(1);
+ }
+
+ ExitThread(0);
+}
+
+VOID
+SetEventHandlers(
+ IN BOOL fSet
+ )
+{
+ SetConsoleCtrlHandler((PVOID)EventHandlerRoutine, fSet);
+}
+
+int
+GetCWD(
+ size_t size,
+ char *CurrentDir
+ )
+{
+ char *pch, *pch2, *pch3, save, save2, save3;
+ HANDLE d;
+ WIN32_FIND_DATA FindData;
+
+ (void)GetCurrentDirectory(size, CurrentDir);
+
+ //
+ // Make sure the drive letter is upper-case.
+ //
+
+ CurrentDir[0] = toupper(CurrentDir[0]);
+
+ //
+ // Go through the path a component at a time, and make
+ // sure that the directory names are in the correct
+ // case.
+ //
+
+ pch = strchr(CurrentDir, '\\');
+ if (NULL == pch) {
+ // we are in the root
+ return 0;
+ }
+ ++pch;
+
+ for (;;) {
+ pch2 = strchr(pch, '\\');
+ if (NULL != pch2)
+ *pch2 = '\0';
+
+ d = FindFirstFile(CurrentDir, &FindData);
+ if (INVALID_HANDLE_VALUE == d) {
+ return -1;
+ }
+ FindClose(d);
+
+ strcpy(pch, FindData.cFileName);
+ if (NULL != pch2) {
+ *pch2 = '\\';
+ pch = pch2 + 1;
+ } else {
+ break;
+ }
+ }
+ return 0;
+}
diff --git a/private/posix/programs/psxses/posix.rc b/private/posix/programs/psxses/posix.rc
new file mode 100644
index 000000000..ee94ba547
--- /dev/null
+++ b/private/posix/programs/psxses/posix.rc
@@ -0,0 +1,19 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Posix Start Application"
+#define VER_INTERNALNAME_STR "posix.exe"
+
+#include "common.ver"
+
+#include "posixres.h"
+
+STRINGTABLE BEGIN
+ IDS_ON "on"
+ IDS_OFF "off"
+END
+
+1 11 MSG00001.bin
diff --git a/private/posix/programs/psxses/posixres.h b/private/posix/programs/psxses/posixres.h
new file mode 100644
index 000000000..36192a0ab
--- /dev/null
+++ b/private/posix/programs/psxses/posixres.h
@@ -0,0 +1,4 @@
+// Include file for resource definitions
+
+#define IDS_ON 100
+#define IDS_OFF 101
diff --git a/private/posix/programs/psxses/psxses.h b/private/posix/programs/psxses/psxses.h
new file mode 100644
index 000000000..b68429a62
--- /dev/null
+++ b/private/posix/programs/psxses/psxses.h
@@ -0,0 +1,114 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ psxses.h
+
+Abstract:
+
+ Main header file for PSXSES module.
+ This module contains includes for both WIN32 and native NT modules.
+ Most files are clean WIN32 sources. files named nt* contain NT
+ calls and provides the interaction with psx server and client.
+
+Author:
+
+ Avi Nathan (avin) 17-Jul-1991
+
+Environment:
+
+ User Mode Only
+
+Revision History:
+
+ Ellen Aycock-Wright (ellena) 15-Sept-1991 Modified for POSIX
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include "sesport.h"
+#include "util.h"
+
+
+HANDLE PsxSSPortHandle;
+HANDLE PsxSessionPort; // psxses side listner and reply
+HANDLE PsxSessionDataSectionHandle;
+HANDLE SSSessionHandle;
+PVOID PsxSessionDataBase;
+
+#define PSX_SESSION_PORT_MEMORY_SIZE 0x10000L //BUG BUG Should this be >?
+
+// Stuff for psx terminal emulator
+
+HANDLE hConsoleInput;
+HANDLE hConsoleOutput;
+
+extern BOOLEAN DoTrickyIO;
+extern BOOLEAN bStop;
+
+extern CRITICAL_SECTION StopMutex;
+extern HANDLE hStopEvent;
+
+extern COORD TrackedCoord;
+
+extern SHORT ScreenRowNum, ScreenColNum; // screen dimension
+
+DWORD TermioInit(void);
+
+#if DBG
+BOOL fVerbose;
+BOOL fTrace;
+#endif
+
+#define CTRL(c) ((c) & 0x1f)
+
+/*
+ * address of the shared memory section of the console port.
+ */
+PVOID PsxSesConPortBaseAddress;
+
+DWORD InitPsxSessionPort(VOID);
+BOOL StartProcess(DWORD SessionPortHandle,
+ char *PgmName,
+ char *CurrentDir,
+ int argc,
+ char **args,
+ char **envp);
+
+DWORD WINAPI ServeSessionRequests(LPVOID Parameter);
+BOOL ServeTmRequest(PSCTMREQUEST PReq, PVOID PStatus);
+BOOL ServeTcRequest(PSCTCREQUEST PReq, PVOID PStatus);
+BOOL ServeConRequest(PSCCONREQUEST PReq, PDWORD PStatus);
+DWORD GetPsxChar(OUT PCHAR AsciiChar);
+
+VOID TerminateSession(ULONG ExitStatus);
+
+BOOL EventHandlerRoutine(IN DWORD CtrlType);
+VOID SetEventHandlers(IN BOOL fSet);
+
+DWORD AnsiInput(OUT LPSTR DestStr, IN DWORD cnt);
+DWORD AnsiOutput(OUT LPSTR SourceStr, IN DWORD cnt);
+ssize_t TermInput(
+ IN HANDLE cs,
+ OUT LPSTR DestStr,
+ IN DWORD cnt,
+ IN int flags,
+ OUT int *pError
+ );
+DWORD TermOutput(IN HANDLE cs, OUT LPSTR SourceStr, IN DWORD cnt);
+
+VOID SignalSession(int SignalType);
+
+typedef struct _CLIENT_AND_PORT {
+ LIST_ENTRY Links;
+ CLIENT_ID ClientId;
+ HANDLE CommPort;
+} CLIENT_AND_PORT, *PCLIENT_AND_PORT;
+
+LIST_ENTRY ClientPortsList;
diff --git a/private/posix/programs/psxses/sources b/private/posix/programs/psxses/sources
new file mode 100644
index 000000000..9dbf8a240
--- /dev/null
+++ b/private/posix/programs/psxses/sources
@@ -0,0 +1,59 @@
+!IF 0
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Ellen Aycock-Wright 15-Sept-1991
+
+NOTE: ??? Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=psxpgms
+MINORCOMP=psxses
+
+TARGETNAME=posix
+TARGETPATH=obj
+TARGETTYPE=PROGRAM
+USE_LIBCMT=1
+
+INCLUDES=..\..\inc
+
+SOURCES= \
+ conreqst.c \
+ error.c \
+ kbdutil.c \
+ ntinitss.c \
+ ntreqst.c \
+ posix.c \
+ posix.rc \
+ tmreqst.c \
+ util.c \
+ tcreqst.c \
+ terminp.c \
+ termoutp.c \
+ posix.rc
+
+C_DEFINES=-DWIN32=1 -DNODEB=1
+
+UMTYPE=console
+
+TARGETLIBS= \
+ $(BASEDIR)\public\sdk\lib\*\smdll.lib \
+ $(BASEDIR)\public\sdk\lib\*\user32.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntdll.lib
+
+NTTARGETFILE0=errors.h errors.mc errors.rc
diff --git a/private/posix/programs/psxses/tcreqst.c b/private/posix/programs/psxses/tcreqst.c
new file mode 100644
index 000000000..22dfdf143
--- /dev/null
+++ b/private/posix/programs/psxses/tcreqst.c
@@ -0,0 +1,86 @@
+#define WIN32_ONLY
+#include <posix/sys/types.h>
+#include <posix/termios.h>
+#include "psxses.h"
+#include <io.h>
+#include <stdio.h>
+#include <memory.h>
+#include <string.h>
+
+DWORD InputModeFlags;
+DWORD OutputModeFlags; /* Console Output Mode */
+unsigned char AnsiNewMode;
+struct termios SavedTermios;
+
+BOOL
+ServeTcRequest(
+ PSCTCREQUEST PReq,
+ PVOID PStatus
+ )
+{
+ DWORD Rc = 0;
+
+ // BUGBUG! error code and returned Status are wrong
+
+ switch ( PReq->Request ) {
+
+ case TcGetAttr:
+ memcpy(&PReq->Termios, &SavedTermios, sizeof(struct termios));
+ break;
+
+ case TcSetAttr:
+ // Don't play this game if the tricky input stuff hasn't been
+ // enabled.
+
+ if (!DoTrickyIO)
+ return 0;
+
+ AnsiNewMode = TRUE;
+ memcpy(&SavedTermios, &PReq->Termios, sizeof(struct termios));
+ InputModeFlags = 0;
+
+ if (PReq->Termios.c_lflag & ICANON) {
+ InputModeFlags |= ENABLE_LINE_INPUT;
+ } else {
+ InputModeFlags &= ~ENABLE_LINE_INPUT;
+ }
+
+ if (PReq->Termios.c_lflag & ECHO) {
+
+ // If you want ECHO_INPUT, you need LINE_INPUT, too.
+
+ InputModeFlags |= (ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
+ } else {
+ InputModeFlags &= ~ENABLE_ECHO_INPUT;
+ }
+
+ if (PReq->Termios.c_lflag & ISIG)
+ InputModeFlags |= ENABLE_PROCESSED_INPUT;
+ else
+ InputModeFlags &= ~ENABLE_PROCESSED_INPUT;
+
+ if (!SetConsoleMode(hConsoleInput, InputModeFlags)) {
+ KdPrint(("PSXSES: SetConsoleMode: %d\n", GetLastError()));
+ *(PDWORD)PStatus = (DWORD)-1L;
+ return 1;
+ }
+
+ break;
+
+ default:
+ *(PDWORD)PStatus = (DWORD)-1L; //STATUS_INVALID_PARAMETER;
+ Rc = 1;
+ break;
+ }
+
+#if 1
+ *(PDWORD) PStatus = (Rc) ? GetLastError() : 0;
+#else
+ if ( !Rc ) {
+ *(PDWORD) PStatus = 0;
+ } else {
+ *(PDWORD) PStatus = GetLastError();
+ }
+#endif
+ return(TRUE);
+}
diff --git a/private/posix/programs/psxses/terminp.c b/private/posix/programs/psxses/terminp.c
new file mode 100644
index 000000000..b61666af4
--- /dev/null
+++ b/private/posix/programs/psxses/terminp.c
@@ -0,0 +1,715 @@
+#define WIN32_ONLY
+#include "psxses.h"
+#include "ansiio.h"
+#include <posix/sys/types.h>
+#include <posix/termios.h>
+#include <io.h>
+#include <stdio.h>
+
+#define _POSIX_
+#include <posix/unistd.h> // for _POSIX_VDISABLE
+#include <posix/sys/errno.h> // for EAGAIN
+
+/* Keyboard Input Buffering Support */
+#define KBD_BUFFER_SIZE 512 // XXX.mjb: should use {MAX_INPUT}
+#define INPUT_EVENT_CLUSTER 1
+
+#define CR 0x0d // carriage return
+#define NL 0x0a // newline (aka line feed)
+
+//
+// We keep an array of screen positions of tab characters. These
+// are used when backspacing over tabs to know where to position
+// the cursor. NumTabs is the number of markers in use.
+//
+#define NUM_TAB_MARKERS (KBD_BUFFER_SIZE)
+/*STATIC*/ int TabMarkers[NUM_TAB_MARKERS];
+/*STATIC*/ int NumTabs;
+
+extern DWORD InputModeFlags; // console input mode
+extern DWORD OutputModeFlags; // Console Output Mode
+extern unsigned char AnsiNewMode;
+extern struct termios SavedTermios; // saved tty settings
+extern HANDLE
+ hIoEvent,
+ hStopEvent,
+ hCanonEvent;
+
+extern CRITICAL_SECTION StopMutex;
+
+/*STATIC*/ int InputEOF; // input at EOF.
+/*STATIC*/ INT DelKbdBuffer(CHAR *CharBuf, DWORD CharCnt);
+
+//
+// SignalInterrupt is also protected by KbdBufMutex; when set,
+// indicates that EINTR should be returned from read.
+//
+/*STATIC*/ int SignalInterrupt = FALSE;
+extern CRITICAL_SECTION KbdBufMutex;
+
+//
+// XXX.mjb: Should implement a dynamic structure so there is no
+// limitation on input (if buffer is full, cannot terminate process
+// with ^C or other interrupt character
+//
+
+CHAR KbdBuffer[KBD_BUFFER_SIZE];
+DWORD KbdBufferHead = 0, // position of queue head
+ KbdBufferTail = 0, // position of queue tail
+ KbdBufferCount = 0, // chars in queue
+ LineCount = 0; // lines in queue
+
+//
+// TermInput -- get input from kbd buffer and return to user.
+//
+// Return number of bytes retrieved; -1 indicates EINTR
+// should occur.
+//
+
+ssize_t
+TermInput(
+ IN HANDLE cs,
+ OUT LPSTR DestStr,
+ IN DWORD cnt,
+ IN int flags,
+ OUT int *pError
+ )
+{
+ DWORD BytesRead = 0;
+ BOOL bSuccess;
+ BOOL StopInput = FALSE;
+ CHAR *PDestStr = (CHAR *)DestStr;
+ CHAR AsciiChar;
+
+ //
+ // Deal with non-blocking input. If canonical mode is set,
+ // we return EAGAIN unless an entire line is available. If
+ // canonical mode is not set, we return EAGAIN unless at least
+ // one character is available.
+ //
+
+ if (flags & PSXSES_NONBLOCK) {
+ if (SavedTermios.c_lflag & ICANON) {
+ if (0 == LineCount) {
+ *pError = EAGAIN;
+ return -1;
+ }
+ } else {
+ if (0 == KbdBufferCount) {
+ *pError = EAGAIN;
+ return -1;
+ }
+ }
+ }
+
+ //
+ // Handle canonical input on consumer side: don't return until
+ // an entire line is ready to return. We don't wait around to
+ // accumulate a line if EOF has occured on input.
+ //
+
+ if ((SavedTermios.c_lflag & ICANON) && !InputEOF) {
+ WaitForSingleObject(hCanonEvent, INFINITE);
+ }
+
+ if (SignalInterrupt) {
+ EnterCriticalSection(&KbdBufMutex);
+
+ if (SignalInterrupt) {
+ SignalInterrupt = FALSE;
+ LeaveCriticalSection(&KbdBufMutex);
+ ResetEvent(hCanonEvent);
+ *pError = EINTR;
+ return -1;
+ }
+ LeaveCriticalSection(&KbdBufMutex);
+ }
+
+ if (0 == KbdBufferCount && InputEOF) {
+ ResetEvent(hCanonEvent);
+ ResetEvent(hIoEvent);
+
+ InputEOF = FALSE;
+ EnterCriticalSection(&KbdBufMutex);
+ LineCount = 0;
+ LeaveCriticalSection(&KbdBufMutex);
+ *pError = 0;
+ return 0;
+ }
+
+ while (BytesRead < cnt && !StopInput) {
+
+ if (0 == KbdBufferCount && InputEOF) {
+ ResetEvent(hCanonEvent);
+ ResetEvent(hIoEvent);
+
+ EnterCriticalSection(&KbdBufMutex);
+ LeaveCriticalSection(&KbdBufMutex);
+ *pError = 0;
+ return BytesRead;
+ }
+
+ //
+ // Wait for input queue to be non-empty
+ //
+
+ WaitForSingleObject(hIoEvent, INFINITE);
+
+ EnterCriticalSection(&KbdBufMutex);
+ DelKbdBuffer(&AsciiChar, 1);
+ LeaveCriticalSection(&KbdBufMutex);
+
+ if ((SavedTermios.c_lflag & ICANON) && '\n' == AsciiChar) {
+ StopInput = TRUE;
+
+ EnterCriticalSection(&KbdBufMutex);
+
+ //
+ // Only reset canonical processing event when there are NO
+ // lines in input queue.
+ //
+
+ if (--LineCount == 0) {
+ bSuccess = ResetEvent(hCanonEvent);
+ ASSERT(bSuccess);
+ }
+
+ LeaveCriticalSection(&KbdBufMutex);
+ }
+
+ *PDestStr++ = AsciiChar;
+ BytesRead++;
+
+ //
+ // XXX.mjb: In noncanonical mode, return even one character. This is a
+ // fudge on the POSIX standard. Should consider MIN and TIME later.
+ //
+ // XXX.mjb: What if a character is removed from the buffer (by
+ // backspace) between the time we check to see if there is one
+ // and the time we call DelKbdBuffer? Could we end up waiting
+ // on hIoEvent in non-canonical mode after characters have been
+ // read?
+ //
+
+ if (!(SavedTermios.c_lflag & ICANON) && 0 == KbdBufferCount) {
+ *pError = 0;
+ return BytesRead;
+ }
+ }
+
+ *pError = 0;
+ return BytesRead;
+}
+
+//
+// BkspcKbdBuffer - do a backspace, removing a character from the
+// input buffer. Arrange for the character to be removed from
+// the display, if bEcho is set.
+//
+// bEcho - echo the character?
+//
+// Returns:
+//
+// TRUE if a character was removed from the buffer.
+// FALSE otherwise (maybe no chars left in line).
+//
+
+BOOL
+BkspcKbdBuffer(
+ BOOLEAN bEcho
+ )
+{
+ char *kill_char;
+ int prev_pos, i;
+
+ if (bEcho) {
+ kill_char = "\010 \010";
+ } else {
+ kill_char = "\010";
+ }
+
+ if (0 == KbdBufferCount) {
+ /* Empty buffer */
+ return FALSE;
+ }
+
+ if ('\n' == KbdBuffer[KbdBufferTail - 1]) {
+ // bkspc should not erase beyond start of line.
+ return FALSE;
+ }
+
+ KbdBufferCount--;
+ if (--KbdBufferTail < 0)
+ KbdBufferTail = KBD_BUFFER_SIZE - 1;
+
+
+ if ('\t' == KbdBuffer[KbdBufferTail]) {
+ int dif;
+
+ --NumTabs;
+ dif = TrackedCoord.X - TabMarkers[NumTabs];
+
+ if (1 == TrackedCoord.X) {
+ char buf[10];
+ ULONG save;
+
+ // bkspc over tab causes reverse wrap.
+
+ save = OutputModeFlags;
+ OutputModeFlags &= ~ENABLE_WRAP_AT_EOL_OUTPUT;
+
+ sprintf(buf, "\x1b[%d;%dH", TrackedCoord.Y - 1,
+ TabMarkers[NumTabs]);
+ TermOutput(hConsoleOutput, buf, strlen(buf));
+ OutputModeFlags = save;
+ return TRUE;
+ }
+
+
+ for (i = 0; i < dif; ++i) {
+ TermOutput(hConsoleOutput, "\010", 1);
+ }
+ return TRUE;
+ }
+
+ if (1 == TrackedCoord.X) {
+ // remove a character that will cause a reverse wrap.
+ char buf[10];
+ ULONG save;
+
+ // Disable wrap to make cursor positioning right.
+
+ save = OutputModeFlags;
+ OutputModeFlags &= ~ENABLE_WRAP_AT_EOL_OUTPUT;
+
+ sprintf(buf, "\x1b[%d;%dH", TrackedCoord.Y - 1,
+ ScreenColNum + 1);
+ if (bEcho) {
+ strcat(buf, " ");
+ }
+ TermOutput(hConsoleOutput, buf, strlen(buf));
+ OutputModeFlags = save;
+ return TRUE;
+ }
+
+ //
+ // Remove an ordinary character.
+ //
+
+ TermOutput(hConsoleOutput, kill_char, 3);
+ return TRUE;
+}
+
+//
+// KillKbdBuffer - execute a line-kill, removing the last line
+// from the input buffer. Arrange for the killed characters
+// to be removed from the display.
+//
+// Returns:
+//
+// TRUE if some characters were killed.
+// FALSE otherwise.
+//
+BOOL
+KillKbdBuffer(
+ VOID
+ )
+{
+ BOOLEAN bEchoK;
+
+ if (0 == KbdBufferCount) {
+ /* Empty buffer */
+ return FALSE;
+ }
+
+ if ('\n' == KbdBuffer[KbdBufferTail - 1]) {
+ // kill should not erase beyond start of line.
+ return FALSE;
+ }
+
+ bEchoK = ((SavedTermios.c_lflag & ECHOK) == ECHOK);
+
+ while (BkspcKbdBuffer(bEchoK))
+ ;
+
+ return TRUE;
+}
+
+//
+// AddKbdBuffer -- add a character to the input queue.
+//
+// Characters have been typed, and they should be added to the
+// input queue.
+//
+//
+BOOL
+AddKbdBuffer(
+ PCHAR Buf,
+ int Cnt
+ )
+{
+ CHAR *pc, *pcLast;
+ char *kill_char = "\010 \010";
+ int prev_pos, i;
+
+ for (pc = Buf, pcLast = Buf + Cnt; pc < pcLast; pc++) {
+
+ if (KbdBufferCount == KBD_BUFFER_SIZE) {
+ KdPrint(("PSXSES: keyboard buffer overflowed\n"));
+ return FALSE;
+ }
+
+ KbdBuffer[KbdBufferTail] = *pc;
+ KbdBufferCount++;
+
+ if ('\t' == *pc) {
+ //
+ // The user has input a tab. We save the screen position
+ // of the character
+ //
+
+ TabMarkers[NumTabs++] = TrackedCoord.X;
+ }
+
+ if (++KbdBufferTail == KBD_BUFFER_SIZE)
+ KbdBufferTail = 0;
+
+ if (KbdBufferCount == 1 && !SetEvent(hIoEvent)) {
+ KdPrint(("PSXSES: failed to set input synch event: 0x%x\n",
+ GetLastError()));
+ }
+
+ }
+ return TRUE;
+}
+
+INT
+DelKbdBuffer(CHAR *CharBuf, DWORD CharCnt)
+{
+ CHAR *AsciiChar, *LastChar;
+
+ for ( AsciiChar = CharBuf, LastChar = (CharBuf + CharCnt);
+ AsciiChar < LastChar; AsciiChar++ ) {
+
+ if ( !KbdBufferCount ) { /* Empty buffer */
+ return (FALSE);
+ }
+
+ *AsciiChar = KbdBuffer[KbdBufferHead];
+ KbdBufferCount--;
+
+ if ( ++KbdBufferHead == KBD_BUFFER_SIZE )
+ KbdBufferHead = 0;
+
+ /* Buffer becomes empty */
+
+ if ( !KbdBufferCount && !ResetEvent(hIoEvent) ) {
+ KdPrint(("PSXSES: failed to reset input synch event\n"));
+ }
+ } /* for */
+ return (TRUE);
+}
+
+
+//
+// ServeKbdInput --
+//
+// Run as a separate thread for asynchronous console input.
+// Get keydown events from the console window and fill the keyboard
+// buffer structure with ASCII values
+//
+VOID
+ServeKbdInput(LPVOID Unused)
+{
+ BOOL bSuccess;
+ BOOL BackSpaceFound = FALSE;
+ BOOL LineKillFound = FALSE;
+ BOOL bSignalInterrupt = FALSE;
+ INPUT_RECORD inputBuffer[INPUT_EVENT_CLUSTER];
+ PKEY_EVENT_RECORD pKeyEvent;
+ DWORD dwEventsRead; /* number of events actually read */
+ UCHAR LocalBuf[10], AsciiChar;
+ int LocalCnt;
+ LPDWORD count;
+ DWORD i;
+
+ for (;;) {
+ bSuccess = ReadConsoleInput(hConsoleInput, inputBuffer,
+ INPUT_EVENT_CLUSTER, &dwEventsRead);
+ if (!bSuccess) {
+ KdPrint(("posix: ReadConsoleInput: 0x%x\n", GetLastError()));
+ ExitThread(1);
+ }
+
+ for (i = 0; i < dwEventsRead; i++) {
+ LocalCnt = 0;
+ BackSpaceFound = FALSE;
+ LineKillFound = FALSE;
+
+ if (inputBuffer[i].EventType != KEY_EVENT) {
+ continue;
+ }
+
+ pKeyEvent = &inputBuffer[i].Event.KeyEvent;
+
+ if (!pKeyEvent->bKeyDown) {
+ //
+ // Only interested in key-down events.
+ //
+ continue;
+ }
+
+ //
+ // Map a Key event to one or more ASCII characters,
+ // in local buffer. AsciiChar should be maintained
+ // as last char typed.
+ //
+
+ AsciiChar = pKeyEvent->uChar.AsciiChar;
+
+// pKeyEvent->wRepeatCount
+
+ switch (pKeyEvent->wVirtualKeyCode) {
+ case VK_SHIFT:
+ case VK_CONTROL:
+ case VK_MENU:
+ case VK_CAPITAL:
+ case VK_NUMLOCK:
+ case VK_SCROLL:
+
+ //
+ // We're not interested in the fact that one
+ // of these keys has been pressed; we'll deal
+ // with them later as a modifier on a normal
+ // key press.
+ //
+ continue;
+
+ case VK_DELETE:
+ AsciiChar = CTRL('?');
+ break;
+
+ case VK_F1:
+ LocalBuf[LocalCnt++] = (UCHAR)'\200';
+ AsciiChar = '\073';
+ break;
+ case VK_F2:
+ LocalBuf[LocalCnt++] = (UCHAR)'\200';
+ AsciiChar = '\074';
+ break;
+ case VK_F3:
+ LocalBuf[LocalCnt++] = (UCHAR)'\200';
+ AsciiChar = '\075';
+ break;
+ case VK_F4:
+ LocalBuf[LocalCnt++] = (UCHAR)'\200';
+ AsciiChar = '\076';
+ break;
+ case VK_F5:
+ LocalBuf[LocalCnt++] = (UCHAR)'\200';
+ AsciiChar = '\077';
+ break;
+ case VK_F6:
+ LocalBuf[LocalCnt++] = (UCHAR)'\200';
+ AsciiChar = '\100';
+ break;
+ case VK_F7:
+ LocalBuf[LocalCnt++] = (UCHAR)'\200';
+ AsciiChar = '\101';
+ break;
+ case VK_F8:
+ LocalBuf[LocalCnt++] = (UCHAR)'\200';
+ AsciiChar = '\102';
+ break;
+ case VK_F9:
+ LocalBuf[LocalCnt++] = (UCHAR)'\200';
+ AsciiChar = '\103';
+ break;
+ case VK_F10:
+ LocalBuf[LocalCnt++] = (UCHAR)'\200';
+ AsciiChar = '\104';
+ break;
+
+ case VK_LEFT:
+ LocalBuf[LocalCnt++] = ANSI_ESC;
+ LocalBuf[LocalCnt++] = '[';
+ AsciiChar = 'D';
+ break;
+ case VK_UP:
+ LocalBuf[LocalCnt++] = ANSI_ESC;
+ LocalBuf[LocalCnt++] = '[';
+ AsciiChar = 'A';
+ break;
+ case VK_RIGHT:
+ LocalBuf[LocalCnt++] = ANSI_ESC;
+ LocalBuf[LocalCnt++] = '[';
+ AsciiChar = 'C';
+ break;
+ case VK_DOWN:
+ LocalBuf[LocalCnt++] = ANSI_ESC;
+ LocalBuf[LocalCnt++] = '[';
+ AsciiChar = 'B';
+ break;
+
+ default:
+ break;
+ }
+
+ if (pKeyEvent->dwControlKeyState &
+ (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) {
+
+ //
+ // Convert Control + key to ^key.
+ //
+
+ AsciiChar = CTRL(AsciiChar);
+ }
+
+ if (SavedTermios.c_iflag & ISTRIP) {
+ //
+ // XXX.mjb: not sure if we should strip here or after
+ // special character processing.
+ //
+
+ AsciiChar &= 0x7F;
+ }
+
+ if ((SavedTermios.c_iflag & IXOFF) ||
+ (SavedTermios.c_iflag & IXON)) {
+ if (AsciiChar == SavedTermios.c_cc[VSTOP]) {
+ EnterCriticalSection(&StopMutex);
+ bStop = TRUE;
+ ResetEvent(hStopEvent);
+ LeaveCriticalSection(&StopMutex);
+ goto discard;
+ }
+ if (AsciiChar == SavedTermios.c_cc[VSTART]) {
+ EnterCriticalSection(&StopMutex);
+ bStop = FALSE;
+ SetEvent(hStopEvent);
+ LeaveCriticalSection(&StopMutex);
+ goto discard;
+ }
+ }
+
+ if (SavedTermios.c_lflag & ISIG) {
+ if (AsciiChar == SavedTermios.c_cc[VINTR] &&
+ SavedTermios.c_cc[VINTR] != _POSIX_VDISABLE) {
+ //
+ // Send an interrupt to all processes in the
+ // foreground process group
+ //
+
+ bSignalInterrupt = TRUE;
+ SignalSession(PSX_SIGINT);
+ goto discard;
+ }
+ if (AsciiChar == SavedTermios.c_cc[VSUSP] &&
+ SavedTermios.c_cc[VSUSP] != _POSIX_VDISABLE) {
+ SignalSession(PSX_SIGTSTP);
+ bSignalInterrupt = TRUE;
+ goto discard;
+ }
+ if (AsciiChar == SavedTermios.c_cc[VQUIT] &&
+ SavedTermios.c_cc[VQUIT] != _POSIX_VDISABLE) {
+ SignalSession(PSX_SIGQUIT);
+ bSignalInterrupt = TRUE;
+ goto discard;
+ }
+ }
+
+ if (SavedTermios.c_lflag & ICANON) {
+ if (AsciiChar == CR) {
+ if ((SavedTermios.c_iflag & ICRNL) &&
+ !(SavedTermios.c_iflag & IGNCR)) {
+ AsciiChar = NL;
+ }
+ }
+ if (AsciiChar == NL) {
+ if (SavedTermios.c_iflag & INLCR) {
+ AsciiChar = CR;
+
+ //XXX.mjb: process this CR?
+ }
+ }
+
+ if (AsciiChar == SavedTermios.c_cc[VKILL] &&
+ SavedTermios.c_cc[VKILL] != _POSIX_VDISABLE) {
+ LineKillFound = TRUE;
+ goto discard;
+ }
+ if (AsciiChar == SavedTermios.c_cc[VERASE] &&
+ SavedTermios.c_cc[VERASE] != _POSIX_VDISABLE) {
+ // character erase
+
+ BackSpaceFound = TRUE;
+ goto discard;
+ }
+ if (AsciiChar == SavedTermios.c_cc[VEOF] &&
+ SavedTermios.c_cc[VEOF] != _POSIX_VDISABLE) {
+ // End of file.
+ AsciiChar = '\n'; // force end-of-line
+ InputEOF = TRUE;
+ goto discard;
+ }
+ if (AsciiChar == SavedTermios.c_cc[VEOL] &&
+ SavedTermios.c_cc[VEOL] != _POSIX_VDISABLE) {
+ // End of line.
+ AsciiChar = '\n'; // force end-of-line
+ }
+ }
+
+ LocalBuf[LocalCnt++] = AsciiChar;
+discard:
+
+ EnterCriticalSection(&KbdBufMutex);
+ if (BackSpaceFound) {
+ BOOL bEchoE = ((SavedTermios.c_lflag & ECHOE) == ECHOE);
+ BkspcKbdBuffer(bEchoE);
+ } else if (LineKillFound) {
+ KillKbdBuffer();
+ } else {
+ AddKbdBuffer(LocalBuf, LocalCnt);
+ }
+ if (SavedTermios.c_lflag & ECHO) {
+ TermOutput(hConsoleOutput, LocalBuf, LocalCnt);
+ }
+
+ if (bSignalInterrupt || InputEOF) {
+
+ //
+ // Blocked reads should be unblocked, to return
+ // either the data that has been accumulated (EOF)
+ // or EINTR (Signal)
+ //
+
+ SignalInterrupt = bSignalInterrupt;
+
+ bSuccess = SetEvent(hCanonEvent);
+ ASSERT(bSuccess);
+ bSuccess = SetEvent(hIoEvent);
+ ASSERT(bSuccess);
+
+ bSignalInterrupt = FALSE;
+ }
+
+ if (AsciiChar == '\n' && (SavedTermios.c_lflag & ICANON)
+ && ++LineCount == 1) {
+
+ //
+ // If we've just finished a new line, signal any
+ // readers who were waiting on a complete line.
+ //
+
+ bSuccess = SetEvent(hCanonEvent);
+ ASSERT(bSuccess);
+
+ // Reset the keeping-track of tabs.
+
+ NumTabs = 0;
+ }
+
+ LeaveCriticalSection(&KbdBufMutex);
+ }
+ }
+}
diff --git a/private/posix/programs/psxses/termoutp.c b/private/posix/programs/psxses/termoutp.c
new file mode 100644
index 000000000..ecc1d0e83
--- /dev/null
+++ b/private/posix/programs/psxses/termoutp.c
@@ -0,0 +1,837 @@
+#define WIN32_ONLY
+#include <posix/sys/types.h>
+#include <posix/termios.h>
+#include "psxses.h"
+#include "ansiio.h"
+#include <io.h>
+#include <stdio.h>
+#include <ctype.h>
+
+extern DWORD OutputModeFlags; /* Console Output Mode */
+extern DWORD InputModeFlags;
+extern unsigned char AnsiNewMode;
+extern struct termios SavedTermios;
+
+/* DFC: New globals (from trans.h) */
+WORD ansi_attr; /* attribute of TTY */
+WORD ansi_attr1; /* MSKK : leave space for 3 attr */
+WORD ansi_attr2; /* MSKK : leave space for 3 attr */
+SHORT ScreenColNum; /* col number */
+SHORT ScreenRowNum; /* row number */
+BYTE CarriageReturn;
+
+COORD TrackedCoord,
+ CurrentCoord;
+
+DWORD TTYConBeep(void);
+
+static BYTE ColorTable[8] = { 0, /* Black */
+ 4, /* Red */
+ 2, /* Green */
+ 6, /* Yellow */
+ 1, /* Blue */
+ 5, /* Magenta */
+ 3, /* Cyan */
+ 7}; /* White */
+
+DWORD
+TermioInit(void)
+{
+ CONSOLE_SCREEN_BUFFER_INFO ScreenInfo1;
+ BOOL Success;
+
+ ansi_state = NOCMD; /* state of machine */
+ ignore_next_char = 0;
+#if 0
+ ansi_base = ansi_attr = 0x07; /* white on black */
+#endif
+ ansi_reverse = 0;
+
+ InputModeFlags = (ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
+ OutputModeFlags ^= ENABLE_WRAP_AT_EOL_OUTPUT;
+ AnsiNewMode = FALSE;
+
+ if (!SetConsoleMode(hConsoleInput, InputModeFlags)) {
+ KdPrint(("posix - can't set console mode: 0x%x\n", GetLastError()));
+ }
+
+ /* Set default termio parameters */
+ SavedTermios.c_iflag = BRKINT|ICRNL;
+ SavedTermios.c_oflag = OPOST|ONLCR;
+ SavedTermios.c_cflag = CREAD|CS8;
+ SavedTermios.c_lflag = ICANON|ECHO|ECHOE|ECHOK|ISIG;
+
+ SavedTermios.c_cc[VEOF] = CTRL('Z');
+ SavedTermios.c_cc[VEOL] = 0; // _POSIX_VDISABLE
+ SavedTermios.c_cc[VERASE] = CTRL('H');
+ SavedTermios.c_cc[VINTR] = CTRL('C');
+ SavedTermios.c_cc[VKILL] = CTRL('X');
+ SavedTermios.c_cc[VQUIT] = CTRL('\\');
+ SavedTermios.c_cc[VSUSP] = CTRL('Y');
+ SavedTermios.c_cc[VSTOP] = CTRL('S');
+ SavedTermios.c_cc[VSTART] = CTRL('Q');
+
+ SavedTermios.c_ospeed = B9600;
+ SavedTermios.c_ispeed = B9600;
+
+ Success = GetConsoleScreenBufferInfo(hConsoleOutput, &ScreenInfo1);
+ if (!Success) {
+ return Success;
+ }
+
+ ansi_base = ansi_attr = ScreenInfo1.wAttributes;
+ ansi_attr1 = ansi_attr2 = 0;
+
+ ScreenRowNum = ScreenInfo1.dwSize.Y;
+ ScreenColNum = ScreenInfo1.dwSize.X;
+
+ CurrentCoord.Y = TrackedCoord.Y = ScreenInfo1.dwCursorPosition.Y + 1;
+ CurrentCoord.X = TrackedCoord.X = ScreenInfo1.dwCursorPosition.X + 1;
+
+ return (DWORD) 0;
+}
+
+/*
+** TermOutput(SourStr, cnt) - pass characters to the finite state machine
+** SourStr points to the array of characters
+** cnt indicates how many characters are being passed.
+*/
+
+DWORD
+TermOutput(
+ IN HANDLE cs,
+ IN LPSTR SourStr,
+ IN DWORD cnt)
+{
+ register CHAR c;
+ USHORT NewCoord, ToFlash;
+ DWORD Rc = 0, orig_cnt = cnt;
+ BOOL SetModeOn, OldWrap, NewWrap;
+ CONSOLE_SCREEN_BUFFER_INFO ScreenInfo1;
+
+ //
+ // Kludge because we don't know how many carriage returns we received
+ // as input, thereby impeding TermOutput()'s ability to track the
+ // cursor coordinate accurately
+ //
+
+ GetConsoleScreenBufferInfo(cs, &ScreenInfo1);
+ CurrentCoord.Y = TrackedCoord.Y = ScreenInfo1.dwCursorPosition.Y + 1;
+ CurrentCoord.X = TrackedCoord.X = ScreenInfo1.dwCursorPosition.X + 1;
+
+ NewCoord = 0;
+ CarriageReturn = 0;
+ TTYOldCtrlCharInStr = TRUE;
+ TTYCtrlCharInStr = FALSE;
+ TTYcs = cs;
+ TTYTextPtr = SourStr;
+ TTYNumBytes = 0;
+
+ //
+ // Stop output, if VSTOP has been encountered
+ //
+
+ if (bStop) {
+ RtlEnterCriticalSection(&StopMutex);
+ if (bStop) {
+ RtlLeaveCriticalSection(&StopMutex);
+ WaitForSingleObject(hStopEvent, INFINITE);
+ } else {
+ RtlLeaveCriticalSection(&StopMutex);
+ }
+ }
+
+ while (cnt--) {
+ c = *SourStr++;
+
+ switch (ansi_state) {
+ case NOCMD:
+ if (c == ANSI_ESC) {
+ //
+ // Make sure buffer is flushed and cursor position is
+ // up-to-date before processing next esc-seq
+ //
+ if ( (Rc = TTYFlushStr(&NewCoord, "1")) != 0 ) {
+ KdPrint(("PSXSES(trans-TTY): failed on "
+ "TTYFlushStr #1\n"));
+ return (DWORD) -1;
+ }
+ ansi_state = ESCED;
+ break;
+ } else {
+ if (isprint(c)) { /* Printable char found */
+
+ TTYNumBytes++;
+ TrackedCoord.X++;
+
+ } else {
+ /* Non-printable char found */
+
+ ToFlash = TRUE;
+ switch ( c ) {
+ case '\n':
+new_line:
+ if (SavedTermios.c_oflag & OPOST) {
+ if (c == '\n' &&
+ (SavedTermios.c_oflag & ONLCR)) {
+ TrackedCoord.Y++;
+ goto carriage_return;
+ }
+ if (SavedTermios.c_oflag & ONLRET) {
+#if 0
+ TrackedCoord.X = 1;
+#endif
+ CarriageReturn = 1;
+ }
+ }
+ TrackedCoord.Y++;
+ NewCoord = 1;
+ break;
+
+ case '\r':
+carriage_return:
+ if ( SavedTermios.c_oflag & OPOST ) {
+ if ( c == '\r' &&
+ (SavedTermios.c_oflag & OCRNL) ) {
+ goto new_line;
+ } else if ( ! (SavedTermios.c_oflag & ONOCR) ||
+ TrackedCoord.X != 1 ) {
+#if 0
+ TrackedCoord.X = 1;
+#endif
+ NewCoord = 1;
+ CarriageReturn = 1;
+ }
+ } else {
+ if ( TrackedCoord.X > 1 ) {
+ TrackedCoord.X = 1;
+ NewCoord = 1;
+ CarriageReturn = 1;
+ }
+ }
+ break;
+
+ case '\b':
+ if ( TrackedCoord.X > 1 ) {
+ TrackedCoord.X--;
+ NewCoord = 1;
+ }
+ break;
+
+ case '\t':
+ TrackedCoord.X += (8 - ((TrackedCoord.X - 1) % 8));
+
+ // Handle wrap after tab
+
+ if (TrackedCoord.X > ScreenColNum) {
+ if (OutputModeFlags & ENABLE_WRAP_AT_EOL_OUTPUT) {
+ TrackedCoord.Y += ((TrackedCoord.X) / ScreenColNum);
+ TrackedCoord.X = (TrackedCoord.X % ScreenColNum);
+ } else {
+ TrackedCoord.X = ScreenColNum;
+ }
+ }
+ NewCoord = 1;
+ break;
+
+ case '\a':
+ if ( (Rc = TTYConBeep()) != 0 ) {
+ KdPrint(("PSXSES(trans-TTY): failed on "
+ "BEEP\n"));
+ return (DWORD) -1;
+ }
+ break;
+
+ default:
+ TTYCtrlCharInStr = TRUE;
+ ToFlash = FALSE;
+#if 0
+ TTYNumBytes++;
+ TrackedCoord.X++;
+#endif
+ break;
+ } /* switch */
+
+ if ( ToFlash ) { /* Flush */
+ //
+ // Flush carriage control chars to update cursor
+ // position
+ //
+ if ((Rc = TTYFlushStr(&NewCoord, "2")) != 0) {
+ return (DWORD)-1;
+ }
+
+ TTYTextPtr = SourStr;
+ }
+ } /* isprint */
+ } /* ANSI_ESC */
+ break;
+
+ case ESCED:
+ switch ( c ) {
+ case '[':
+ ansi_state = PARAMS;
+ SetModeOn = TRUE;
+ clrparam();
+ break;
+
+ default:
+ ansi_state = NOCMD;
+ TTYTextPtr = SourStr - 1;
+ cnt++ ;
+ SourStr--;
+ break;
+ }
+ break;
+
+ case PARAMS:
+ if ( isdigit(c) ) {
+ ansi_param[ansi_pnum] *= 10;
+ ansi_param[ansi_pnum] += (c - '0');
+ SetModeOn = FALSE;
+ } else if ( c == ';' ) {
+ if ( ansi_pnum < (NPARMS - 1) )
+ ++ansi_pnum;
+ else {
+ ansi_state = NOCMD;
+ TTYTextPtr = SourStr;
+ }
+ } else if ( (c == '=') && SetModeOn ) { /* maybe set/reset mode */
+ ansi_state = MODCMD;
+ } else {
+ ansi_state = NOCMD;
+ if ( (Rc = ansicmd(cs, c)) != 0 ) {
+ return (DWORD) -1;
+ }
+ TTYTextPtr = SourStr;
+ NewCoord = 1;
+ if ( (Rc = TTYFlushStr(&NewCoord, "3")) != 0 ) {
+#if 0
+ return (DWORD) -1;
+#endif
+ }
+ }
+ break;
+
+ case MODCMD:
+ if ( ansi_pnum == 1 ) {
+
+ if ( c == 'h' || c == 'l' ) {
+
+ if ( ansi_param[0] == 7 ) {
+
+ OldWrap = ((OutputModeFlags &
+ ENABLE_WRAP_AT_EOL_OUTPUT) != 0);
+ NewWrap = (c == 'h');
+
+ if ( OldWrap != NewWrap ) {
+
+ if ( (Rc = !SetConsoleMode(cs,
+ OutputModeFlags^ENABLE_WRAP_AT_EOL_OUTPUT))
+ != 0 ) {
+ return (DWORD) -1;
+ }
+
+ OutputModeFlags ^= ENABLE_WRAP_AT_EOL_OUTPUT;
+#if 0
+ } else {
+ OutputModeFlags ~= ENABLE_WRAP_AT_EOL_OUTPUT;
+#endif
+ }
+ }
+
+ TTYTextPtr = SourStr;
+
+ } else {
+ TTYTextPtr = SourStr - 5;
+ TTYNumBytes = 5;
+ }
+
+ } else if ( c >= '0' && c <= '7' ) {
+ ansi_param[0] = (USHORT) (c - '0');
+ ansi_pnum = 1;
+ break;
+ } else {
+ TTYTextPtr = SourStr - 4;
+ TTYNumBytes = 4;
+ }
+ ansi_state = NOCMD;
+ break;
+
+ case MODDBCS:
+ TTYNumBytes++;
+ TrackedCoord.X++;
+ ansi_state = NOCMD;
+ break;
+
+ } /* switch ansi_state */
+
+ } /* while cnt */
+
+ /* Flush */
+ if ( (Rc = TTYFlushStr(&NewCoord, "4")) != 0 ) {
+ return (DWORD) -1;
+ }
+
+ return(orig_cnt);
+}
+
+/*
+** clrparam(lp) - clear the parameters for a screen
+** lp points to the screen's crt struct
+*/
+
+VOID
+clrparam(void)
+{
+ register int i;
+
+ for ( i = 0; i < NPARMS; i += 1 )
+ ansi_param[i] = 0;
+ ansi_pnum = 0;
+}
+
+//
+// lscroll - scroll the sceen
+//
+void
+lscroll(
+ HANDLE h, // handle on the console buffer to scroll
+ int lines // number of lines to scroll (negative means
+ // scroll text down)
+ )
+{
+ COORD coordDest;
+ SMALL_RECT ScrollRect;
+ CHAR_INFO ScrollChar;
+ BOOLEAN Success;
+
+ if (0 == lines) {
+ // already done
+ return;
+ }
+
+ if (lines < 0) {
+ // scroll text down
+ ScrollRect.Top = 0;
+ ScrollRect.Bottom = ScreenRowNum + lines;
+ coordDest.X = 0;
+ coordDest.Y = 0 - lines;
+ } else {
+ // scroll text up
+ ScrollRect.Top = lines;
+ ScrollRect.Bottom = ScreenRowNum;
+ coordDest.X = 0;
+ coordDest.Y = 0;
+ }
+
+ ScrollRect.Left = 0;
+ ScrollRect.Right = ScreenColNum;
+
+ ScrollChar.Attributes = (ansi_attr);
+ ScrollChar.Char.AsciiChar = ' ';
+
+ Success = ScrollConsoleScreenBufferA(h, &ScrollRect, NULL,
+ coordDest, &ScrollChar);
+ if (!Success) {
+ KdPrint(("POSIX: ScrollConsole: 0x%x\n", GetLastError()));
+ }
+ return;
+}
+
+//
+// ansicmd - perform some ANSI 3.64 function, using the parameters
+// we've just gathered.
+//
+// c is the character that indicates the function to be performed
+//
+
+DWORD
+ansicmd(
+ IN HANDLE cs,
+ IN CHAR c
+ )
+{
+ DWORD NumFilled, Rc = 0;
+ USHORT j;
+ COORD Coord;
+
+ switch (c) {
+ case ANSI_CUB: /* cursor backward */
+ TrackedCoord.X -= range(ansi_param[0], 1, 1, TrackedCoord.X - 1);
+ break;
+
+ case ANSI_CUF: /* cursor forward */
+ TrackedCoord.X += range(ansi_param[0], 1, 1,
+ ScreenColNum - TrackedCoord.X );
+ break;
+
+ case ANSI_CUU: /* cursor up */
+ TrackedCoord.Y -= range(ansi_param[0], 1, 1, TrackedCoord.Y - 1);
+ break;
+
+ case ANSI_CUD: /* cursor down */
+ TrackedCoord.Y += range(ansi_param[0], 1, 1,
+ ScreenRowNum - TrackedCoord.Y);
+ break;
+
+ case ANSI_CUP: /* cursor position */
+ case ANSI_CUP1:
+ TrackedCoord.Y = (USHORT) range(ansi_param[0], 1, 1, ScreenRowNum);
+ TrackedCoord.X = (USHORT) range(ansi_param[1], 1, 1, ScreenColNum);
+ break;
+
+ case ANSI_ED: /* erase display */
+ switch ( ansi_param[0] ) {
+ case 2:
+ TrackedCoord.Y = TrackedCoord.X = 1;
+ Coord.X = (SHORT) (TrackedCoord.X - 1);
+ Coord.Y = (SHORT) (TrackedCoord.Y - 1);
+ if ( (Rc = (!FillConsoleOutputCharacterA(cs, ' ',
+ (DWORD) ScreenRowNum * ScreenColNum, Coord, &NumFilled)))
+ != 0 ) {
+ return (Rc);
+ }
+
+ if ( (Rc = (!FillConsoleOutputAttribute(cs, (ansi_attr),
+ NumFilled, Coord, &NumFilled))) != 0 ) {
+
+ return (Rc);
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case ANSI_EL:
+ Coord.X = (SHORT)(TrackedCoord.X - 1);
+ Coord.Y = (SHORT)(TrackedCoord.Y - 1);
+
+ switch ( ansi_param[0] ) {
+ case 0: /* up to end */
+ if ( (Rc = (!FillConsoleOutputCharacterA(cs, ' ',
+ (DWORD) (ScreenColNum - Coord.X), Coord, &NumFilled))) != 0 ) {
+ return (Rc);
+ }
+
+ if ( (Rc = (!FillConsoleOutputAttribute(cs, (ansi_attr),
+ NumFilled, Coord, &NumFilled))) != 0 ) {
+
+ return (Rc);
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case ANSI_SGR:
+ // SGR = Select Graphic Rendition
+
+ for ( j = 0; (SHORT) j <= (SHORT) ansi_pnum; j++ ) {
+ SetTTYAttr(cs, ansi_param[j]);
+ }
+ break;
+
+#if 0
+ case ANSI_SCP:
+ ansi_scp = TrackedCoord;
+ break;
+
+ case ANSI_RCP:
+ TrackedCoord = ansi_scp;
+ break;
+ case ANSI_CPL: /* cursor to previous line */
+ TrackedCoord.Y -= range(ansi_param[0], 1, 1, ScreenRowNum);
+ TrackedCoord.X = 1;
+ break;
+
+ case ANSI_CNL: /* cursor to next line */
+ TrackedCoord.Y += range(ansi_param[0], 1, 1, ScreenRowNum);
+ TrackedCoord.X = 1;
+ break;
+
+ case ANSI_CBT: /* tab backwards */
+ col = TrackedCoord.X - 1;
+ i = range(ansi_param[0], 1, 1, (col + 7) >> 3);
+ if ( col & 7 ) {
+ TrackedCoord.X = (col & ~7) + 1;
+ --i;
+ }
+ TrackedCoord.X -= (i << 3);
+ break;
+
+ case ANSI_DCH: /* delete character */
+ ansi_param[0] = range(ansi_param[0], 1, 1,
+ (ScreenColNum - TrackedCoord.X) + 1);
+ if ( TrackedCoord.X + ansi_param[0] <= ScreenColNum ) {
+ lcopy(cs, lp, TrackedCoord.X+ansi_param[0]-1, TrackedCoord.Y-1,
+ TrackedCoord.X-1, TrackedCoord.Y-1,
+ ScreenColNum-(TrackedCoord.X+ansi_param[0]-1));
+ }
+ lclear(cs, lp, ScreenColNum-ansi_param[0], TrackedCoord.Y-1,
+ ansi_param[0], SA_BONW);
+ break;
+
+ case ANSI_DL: /* delete line */
+ ansi_param[0] = range(ansi_param[0], 1, 1,
+ (ScreenRowNum - TrackedCoord.Y) + 1);
+ /* copy lines up */
+ if ( TrackedCoord.Y + ansi_param[0] <= ScreenRowNum ) {
+ lcopy(cs, lp, 0, TrackedCoord.Y+ansi_param[0]-1, 0,
+ TrackedCoord.Y-1,
+ ScreenColNum*(ScreenRowNum-(TrackedCoord.Y+ansi_param[0]-1)));
+ }
+ /* clear new stuff */
+ lclear(cs, lp, 0, ScreenRowNum-ansi_param[0],
+ ScreenColNum*ansi_param[0], SA_BONW);
+ break;
+
+ case ANSI_ECH: /* erase character */
+ ansi_param[0] = range( ansi_param[0], 1, 1,
+ (ScreenColNum - TrackedCoord.X) + 1);
+ lclear(cs, lp, TrackedCoord.X-1, TrackedCoord.Y-1, ansi_param[0],
+ SA_BONW);
+ break;
+
+ case ANSI_ICH: /* insert character */
+ ansi_param[0] = range( ansi_param[0], 1, 1,
+ (ScreenColNum - TrackedCoord.X) + 1);
+ if ( TrackedCoord.X + ansi_param[0] <= ScreenColNum ) {
+ lcopy(cs, lp, TrackedCoord.X-1, TrackedCoord.Y-1,
+ TrackedCoord.X+ansi_param[0]-1, TrackedCoord.Y-1,
+ ScreenColNum-(TrackedCoord.X+ansi_param[0]-1));
+ }
+ lclear(cs, lp, TrackedCoord.X-1, TrackedCoord.Y-1, ansi_param[0],
+ SA_BONW);
+ break;
+
+ case ANSI_IL: /* insert line */
+ ansi_param[0] = range(ansi_param[0], 1, 1,
+ (ScreenRowNum - TrackedCoord.Y) + 1);
+ /* copy lines down */
+ if ( TrackedCoord.Y + ansi_param[0] <= ScreenRowNum ) {
+ lcopy(cs, lp, 0, TrackedCoord.Y-1, 0,
+ TrackedCoord.Y+ansi_param[0]-1,
+ ScreenColNum*(ScreenRowNum-(TrackedCoord.Y+ansi_param[0]-1)));
+ }
+ /* clear new stuff */
+ lclear(cs, lp, 0, TrackedCoord.Y-1, ScreenColNum * ansi_param[0],
+ SA_BONW);
+ break;
+#endif
+
+ case ANSI_SU: /* scroll up */
+ ansi_param[0] = range(ansi_param[0], 1, 1, ScreenRowNum);
+ lscroll(cs, ansi_param[0]);
+ break;
+
+ case ANSI_SD: /* scroll down */
+ {
+ int i = -range(ansi_param[0], 1, 1, ScreenRowNum);
+ lscroll(cs, i);
+ }
+ break;
+
+ default:
+ return (DWORD) 0;
+ }
+ return (Rc);
+}
+
+/*
+** range(val, default, min, max) - restrict a value to a range, or supply a
+** default
+** val is the value to be restricted.
+** default is the value to be returned if val is zero
+** min is the minimum value
+** max is the maximum value
+*/
+
+int
+range(int val, int def, int min, int max)
+{
+ if ( val == 0 )
+ return def;
+ if ( val < min )
+ return min;
+ if ( val > max )
+ return max;
+ return val;
+}
+
+DWORD
+SetTTYAttr(IN HANDLE cs, IN USHORT AnsiParm)
+{
+ WORD NewAttr, LastAttr = ansi_attr; /* attribute of TTY */
+ BOOL Rc;
+
+ if ( AnsiParm == 0 ) { // BUGBUG ? or the default is according to Win
+ ansi_base = 0x07; /* white on black */
+ ansi_reverse = 0x00;
+ } else if ( AnsiParm == 7 ) {
+ ansi_reverse = 1;
+ } else if ( ((AnsiParm >= 30) && (AnsiParm <= 37)) ||
+ ((AnsiParm >= 40) && (AnsiParm <= 47)) ) {
+ if ( AnsiParm >= 40 )
+ ansi_base = (BYTE) ((ansi_base & 0x0F) | ( 4 << ColorTable[AnsiParm%10]));
+ else
+ ansi_base = (BYTE) ((ansi_base & 0xF0) | ColorTable[AnsiParm%10]);
+ } else if ( AnsiParm == 8 ) {
+#if 0
+ ansi_cancel = 1;
+#endif
+ } else if ( AnsiParm == 1 ) {
+#if 0
+ ansi_intensity = 1;
+#endif
+ } else if ( AnsiParm == 4 ) {
+#if 0
+ ansi_bold = 1;
+#endif
+ } else if ( AnsiParm == 5 ) {
+#if 0
+ ansi_underscore = 1;
+#endif
+ }
+
+ if ( ansi_reverse )
+ NewAttr = (BYTE) (((ansi_base & 0x0F) << 4 ) |
+ ((ansi_base & 0xF0) >> 4 ));
+ else
+ NewAttr = ansi_base;
+
+ if ( LastAttr != NewAttr ) {
+ /* new attribute */
+ if ( (Rc = !SetConsoleTextAttribute(cs, (NewAttr))) != 0 ) {
+ return (Rc);
+ } else {
+ ansi_attr = NewAttr;
+ }
+ }
+ return (NO_ERROR);
+}
+
+DWORD
+TTYConBeep(void)
+{
+ DWORD NumWritten, Rc;
+ CHAR BeepChar = '\a';
+
+ Rc = !WriteConsoleA(TTYcs, &BeepChar, 1, &NumWritten, NULL);
+
+ return(Rc);
+}
+
+
+DWORD
+TTYFlushStr(USHORT *newcoord, const char *call)
+{
+ DWORD NumWritten;
+ BOOL Success;
+ COORD coordDest, coord;
+ SMALL_RECT ScrollRect;
+ CHAR_INFO ScrollChar;
+
+ if (TTYNumBytes) {
+ if (TrackedCoord.X > ScreenColNum) {
+
+ // Handle cursor tracking of text wrap-around
+
+ if (OutputModeFlags & ENABLE_WRAP_AT_EOL_OUTPUT) {
+#if 0
+ TrackedCoord.Y += ((CurrentCoord.X + TrackedCoord.X)
+ / ScreenColNum);
+#else
+ TrackedCoord.Y += ((TrackedCoord.X) / ScreenColNum);
+#endif
+ TrackedCoord.X = (TrackedCoord.X % ScreenColNum);
+ } else {
+ TrackedCoord.X = ScreenColNum;
+ }
+ *newcoord = 1;
+ } else if (TrackedCoord.X < 1) {
+ TrackedCoord.X = 1;
+#if 0
+ *newcoord = 1;
+#endif
+ }
+ }
+
+ //
+ // Handle scrolling when printing beyond bottom of screen
+ //
+
+ if (TrackedCoord.Y > ScreenRowNum) {
+#if 0
+ ScrollRect.Top = TrackedCoord.Y - ScreenRowNum;
+ ScrollRect.Bottom = (SHORT)ScreenRowNum;
+ ScrollRect.Left = 0;
+ ScrollRect.Right = (SHORT)ScreenColNum;
+ coordDest.X = 0;
+ coordDest.Y = 0;
+ ScrollChar.Attributes = (ansi_attr);
+ ScrollChar.Char.AsciiChar = ' ';
+
+ Success = ScrollConsoleScreenBufferA(TTYcs, &ScrollRect, NULL,
+ coordDest, &ScrollChar);
+ if (!Success) {
+ KdPrint(("POSIX: ScrollConsole: 0x%x\n", GetLastError()));
+ return Success;
+ }
+#else
+ lscroll(TTYcs, TrackedCoord.Y - ScreenRowNum);
+#endif
+
+ if (TTYNumBytes) {
+ coord.X = CurrentCoord.X - 1;
+ coord.Y = ScreenRowNum - (TrackedCoord.Y - ScreenRowNum) - 1;
+
+ Success = WriteConsoleOutputCharacterA(TTYcs, (LPSTR)TTYTextPtr,
+ TTYNumBytes, coord, &NumWritten);
+ if (!Success) {
+ KdPrint(("POSIX: WriteConsoleOutputChar: 0x%x\n", GetLastError()));
+ return Success;
+ }
+
+ TTYNumBytes = 0;
+ }
+ TrackedCoord.Y = ScreenRowNum;
+#if 0
+ *newcoord = 1;
+#endif
+ } else if (TTYNumBytes) {
+
+ // Not printing beyond bottom of screen.
+
+ Success = WriteConsoleA(TTYcs, (LPSTR) TTYTextPtr, TTYNumBytes,
+ &NumWritten, NULL);
+ if (!Success) {
+ return Success;
+ }
+ TTYNumBytes = 0;
+ }
+
+ if (*newcoord) {
+ if (CarriageReturn) {
+ CarriageReturn = 0;
+ TrackedCoord.X = 1;
+ }
+
+ coord.X = TrackedCoord.X - 1;
+ coord.Y = TrackedCoord.Y - 1;
+
+ Success = SetConsoleCursorPosition(TTYcs, coord);
+ if (!Success) {
+ return Success;
+ }
+ *newcoord = 0;
+ }
+
+ CurrentCoord = TrackedCoord;
+
+ return (DWORD)0;
+}
diff --git a/private/posix/programs/psxses/tmreqst.c b/private/posix/programs/psxses/tmreqst.c
new file mode 100644
index 000000000..e26af806e
--- /dev/null
+++ b/private/posix/programs/psxses/tmreqst.c
@@ -0,0 +1,52 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ tmrqust.c
+
+Abstract:
+
+ This module contains the handler for task manager requests.
+
+Author:
+
+ Avi Nathan (avin) 17-Jul-1991
+
+Environment:
+
+ User Mode Only
+
+Revision History:
+
+ Ellen Aycock-Wright (ellena) 15-Sept-1991 Modified for POSIX
+
+--*/
+
+#define WIN32_ONLY
+#include "psxses.h"
+
+BOOL ServeTmRequest(PSCTMREQUEST PReq, PVOID PStatus)
+{
+
+ DWORD Rc;
+
+ switch (PReq->Request) {
+ case TmExit:
+ TerminateSession(PReq->ExitStatus);
+ *(PDWORD) PStatus = 0;
+ return(FALSE);
+ break;
+
+ default:
+ *(PDWORD) PStatus = (unsigned)-1L; // STATUS_INVALID_PARAMETER;
+ Rc = FALSE;
+ }
+
+ *(PDWORD) PStatus = 0;
+ return(TRUE); // Do reply
+}
+
+
+
diff --git a/private/posix/programs/psxses/util.c b/private/posix/programs/psxses/util.c
new file mode 100644
index 000000000..d627146d6
--- /dev/null
+++ b/private/posix/programs/psxses/util.c
@@ -0,0 +1,63 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ util.c
+
+Abstract:
+
+ This module contains the common utilities used in PSXSES module
+
+Author:
+
+ Avi Nathan (avin) 17-Jul-1991
+
+Environment:
+
+ User Mode Only
+
+Revision History:
+
+--*/
+
+
+#define WIN32_ONLY
+#include "psxses.h"
+#include "util.h"
+
+#include <stdio.h>
+#include <direct.h>
+
+#include <windows.h>
+
+DWORD
+GetSessionUniqueId(VOID)
+{
+ return(GetCurrentProcessId());
+}
+
+LPTSTR
+MyLoadString(UINT Id)
+{
+ TCHAR buf[100];
+ PTCHAR ptc;
+ HINSTANCE hInstance;
+ int r;
+
+ hInstance = GetModuleHandle(NULL);
+
+ r = LoadString(hInstance, Id, buf, (int)sizeof(buf));
+ if (0 == r) {
+ // String rsrc doesn't exist.
+ KdPrint(("PSXSES: LoadString: 0x%x\n", GetLastError()));
+ return NULL;
+ }
+
+ ptc = LocalAlloc(0, (r + 1)*sizeof(TCHAR));
+ if (NULL == ptc) {
+ return NULL;
+ }
+ return lstrcpy(ptc, buf);
+}
diff --git a/private/posix/programs/psxses/util.h b/private/posix/programs/psxses/util.h
new file mode 100644
index 000000000..910f8f77b
--- /dev/null
+++ b/private/posix/programs/psxses/util.h
@@ -0,0 +1,31 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ util.h
+
+Abstract:
+
+ Prototypes for functions in util.c.
+
+Author:
+
+ Avi Nathan (avin) 17-Jul-1991
+
+Environment:
+
+ User Mode Only
+
+Revision History:
+
+--*/
+
+DWORD GetSessionUniqueId(VOID);
+
+VOID PrintAssertion(char * Condition, char * File, int Line);
+
+VOID QuerySessionDrives(OUT PVOID Buf);
+
+LPTSTR MyLoadString(UINT Id);
diff --git a/private/posix/psxss/acledit.c b/private/posix/psxss/acledit.c
new file mode 100644
index 000000000..fa0a5af15
--- /dev/null
+++ b/private/posix/psxss/acledit.c
@@ -0,0 +1,523 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ Acledit.c
+
+Abstract:
+
+ This Module implements the Acl rtl editing functions that are defined in
+ ntseapi.h
+
+Author:
+
+ Gary Kimura (GaryKi) 9-Nov-1989
+
+Environment:
+
+ Pure Runtime Library Routine
+
+Revision History:
+
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include "seopaque.h"
+
+//
+// Define the local macros and procedure for this module
+//
+
+//
+// Return a pointer to the first Ace in an Acl (even if the Acl is empty).
+//
+// PACE_HEADER
+// FirstAce (
+// IN PACL Acl
+// );
+//
+
+#define FirstAce(Acl) ((PVOID)((PUCHAR)(Acl) + sizeof(ACL)))
+
+VOID
+AddData (
+ IN PVOID From,
+ IN ULONG FromSize,
+ IN PVOID To,
+ IN ULONG ToSize
+ );
+
+VOID
+DeleteData (
+ IN PVOID Data,
+ IN ULONG RemoveSize,
+ IN ULONG TotalSize
+ );
+
+
+
+NTSTATUS
+RtlMakePosixAcl(
+ IN ULONG AclRevision,
+ IN PSID UserSid,
+ IN PSID GroupSid,
+ IN ACCESS_MASK UserAccess,
+ IN ACCESS_MASK GroupAccess,
+ IN ACCESS_MASK OtherAccess,
+ IN ULONG AclLength,
+ OUT PACL Acl,
+ OUT PULONG ReturnLength
+ )
+/*++
+
+Routine Description:
+
+ NOTE: THIS ROUTINE IS STILL BEING SPEC'D.
+
+ Make an ACL representing Posix protection from AccessMask and
+ security account ID (SID) information.
+
+Arguments:
+
+ AclRevision - Indicates the ACL revision level of the access masks
+ provided. The ACL generated will be revision compatible with this
+ value and will not be a higher revision than this value.
+
+ UserSid - Provides the SID of the user (owner).
+
+ GroupSid - Provides the SID of the primary group.
+
+ UserAccess - Specifies the accesses to be given to the user (owner).
+
+ GroupAccess - Specifies the accesses to be given to the primary group.
+
+ OtherAccess - Specifies the accesses to be given to others (WORLD).
+
+ AclLength - Provides the length (in bytes) of the Acl buffer.
+
+ Acl - Points to a buffer to receive the generated ACL.
+
+ ReturnLength - Returns the actual length needed to store the resultant
+ ACL. If this length is greater than that specified in AclLength,
+ then STATUS_BUFFER_TOO_SMALL is returned and no ACL is generated.
+
+Return Values:
+
+ STATUS_SUCCESS - The service completed successfully.
+
+ STATUS_UNKNOWN_REVISION - The revision level specified is not supported
+ by this service.
+
+ STATUS_BUFFER_TOO_SMALL - Indicates the length of the output buffer
+ wasn't large enough to hold the generated ACL. The length needed
+ is returned via the ReturnLength parameter.
+
+--*/
+
+{
+
+ SID_IDENTIFIER_AUTHORITY WorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY;
+
+ ULONG UserSidLength;
+ ULONG GroupSidLength;
+ ULONG WorldSidLength;
+ ULONG RequiredAclSize;
+ ULONG AceSize;
+ ULONG CurrentAce;
+ PACCESS_ALLOWED_ACE Ace;
+ NTSTATUS Status;
+ PSID WorldSid;
+
+ if (!RtlValidSid( UserSid ) || !RtlValidSid( GroupSid )) {
+ return( STATUS_INVALID_SID );
+ }
+
+ UserSidLength = RtlLengthSid( UserSid );
+ GroupSidLength = RtlLengthSid( GroupSid );
+ WorldSidLength = RtlLengthRequiredSid( 1 );
+
+
+ //
+ // Figure out how much room we need for an ACL and three
+ // ACCESS_ALLOWED Ace's
+ //
+
+ RequiredAclSize = sizeof( ACL );
+
+ AceSize = sizeof( ACCESS_ALLOWED_ACE ) - sizeof( ULONG );
+
+ RequiredAclSize += (AceSize * 3) +
+ UserSidLength +
+ GroupSidLength +
+ WorldSidLength ;
+
+ if (RequiredAclSize > AclLength) {
+ *ReturnLength = RequiredAclSize;
+ return( STATUS_BUFFER_TOO_SMALL );
+ }
+
+ //
+ // The passed buffer is big enough, build the ACL in it.
+ //
+
+ Status = RtlCreateAcl(
+ Acl,
+ RequiredAclSize,
+ AclRevision
+ );
+
+ if (!NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ Status = RtlAddAccessAllowedAce(
+ Acl,
+ ACL_REVISION2,
+ UserAccess,
+ UserSid
+ );
+ if (!NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ Status = RtlAddAccessAllowedAce(
+ Acl,
+ ACL_REVISION2,
+ GroupAccess,
+ GroupSid
+ );
+ if (!NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ Status = RtlAllocateAndInitializeSid(&WorldSidAuthority, 1,
+ SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &WorldSid);
+ if (!NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ Status = RtlAddAccessAllowedAce(
+ Acl,
+ ACL_REVISION2,
+ OtherAccess,
+ WorldSid
+ );
+
+ RtlFreeSid(WorldSid);
+ if (!NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ return( STATUS_SUCCESS );
+
+}
+
+
+
+
+NTSTATUS
+RtlInterpretPosixAcl(
+ IN ULONG AclRevision,
+ IN PSID UserSid,
+ IN PSID GroupSid,
+ IN PACL Acl,
+ OUT PACCESS_MASK UserAccess,
+ OUT PACCESS_MASK GroupAccess,
+ OUT PACCESS_MASK OtherAccess
+ )
+/*++
+
+Routine Description:
+
+ NOTE: THIS ROUTINE IS STILL BEING SPEC'D.
+
+ Interpret an ACL representing Posix protection, returning AccessMasks.
+ Use security account IDs (SIDs) for object owner and primary group
+ identification.
+
+ This algorithm will pick up the first match of a given SID and ignore
+ all further matches of that SID. The first unrecognized SID becomes
+ the "other" SID.
+
+Arguments:
+
+ AclRevision - Indicates the ACL revision level of the access masks to
+ be returned.
+
+ UserSid - Provides the SID of the user (owner).
+
+ GroupSid - Provides the SID of the primary group.
+
+ Acl - Points to a buffer containing the ACL to interpret.
+
+ UserAccess - Receives the accesses allowed for the user (owner).
+
+ GroupAccess - Receives the accesses allowed for the primary group.
+
+ OtherAccess - Receives the accesses allowed for others (WORLD).
+
+Return Values:
+
+ STATUS_SUCCESS - The service completed successfully.
+
+ STATUS_UNKNOWN_REVISION - The revision level specified is not supported
+ by this service.
+
+ STATUS_EXTRENEOUS_INFORMATION - This warning status value indicates the
+ ACL contained protection or other information unrelated to Posix
+ style protection. This is a warning only. The interpretation was
+ otherwise successful and all access masks were returned.
+
+ STATUS_COULD_NOT_INTERPRET - Indicates the ACL does not contain
+ sufficient Posix style (user/group) protection information. The
+ ACL could not be interpreted.
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ BOOLEAN UserFound = FALSE;
+ BOOLEAN GroupFound = FALSE;
+ BOOLEAN OtherFound = FALSE;
+ ULONG i;
+ PKNOWN_ACE Ace;
+
+ *UserAccess = *GroupAccess = *OtherAccess = 0;
+
+ if (AclRevision != ACL_REVISION2) {
+ return( STATUS_UNKNOWN_REVISION );
+ }
+
+ //
+ // Special case for ACLs that are just "Everyone: Full Control".
+ //
+
+ if (Acl->AceCount < 3) {
+ SID_IDENTIFIER_AUTHORITY WorldSidAuth = SECURITY_WORLD_SID_AUTHORITY;
+ PSID WorldSid;
+
+ Status = RtlAllocateAndInitializeSid(&WorldSidAuth, 1,
+ SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &WorldSid);
+ if (!NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ Status = RtlGetAce(Acl, 0, (PVOID *)&Ace);
+ if (!NT_SUCCESS(Status)) {
+ RtlFreeSid(WorldSid);
+ return Status;
+ }
+
+ if (RtlEqualSid((PSID)&Ace->SidStart, WorldSid)) {
+ *UserAccess = *GroupAccess = *OtherAccess = Ace->Mask;
+ RtlFreeSid(WorldSid);
+ return STATUS_SUCCESS;
+ }
+ RtlFreeSid(WorldSid);
+ }
+
+ for (i = 0; i < Acl->AceCount && (!UserFound || !GroupFound || !OtherFound);
+ ++i) {
+ Status = RtlGetAce(Acl, i, (PVOID *)&Ace);
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ if (Ace->Header.AceType != ACCESS_ALLOWED_ACE_TYPE) {
+ Status = STATUS_EXTRANEOUS_INFORMATION;
+ continue;
+ }
+
+ if (RtlEqualSid(
+ (PSID)(&Ace->SidStart),
+ UserSid
+ ) && !UserFound) {
+
+ *UserAccess = Ace->Mask;
+ UserFound = TRUE;
+ continue;
+ }
+
+ if (RtlEqualSid(
+ (PSID)(&Ace->SidStart),
+ GroupSid
+ ) && !GroupFound) {
+ *GroupAccess = Ace->Mask;
+ GroupFound = TRUE;
+ continue;
+ }
+
+ //
+ // It isn't the user, and it isn't the group, pick it up
+ // as "other"
+ //
+
+ if (!OtherFound) {
+ *OtherAccess = Ace->Mask;
+ OtherFound = TRUE;
+ continue;
+ }
+ }
+
+ return( Status );
+
+}
+
+
+//
+// Internal support routine
+//
+
+VOID
+AddData (
+ IN PVOID From,
+ IN ULONG FromSize,
+ IN PVOID To,
+ IN ULONG ToSize
+ )
+
+/*++
+
+Routine Description:
+
+ This routine copies data to a string of bytes. It does this by moving
+ over data in the to string so that the from string will fit. It also
+ assumes that the checks that the data will fit in memory have already
+ been done. Pictorally the results are as follows.
+
+ Before:
+
+ From -> ffffffffff
+
+ To -> tttttttttttttttt
+
+ After:
+
+ From -> ffffffffff
+
+ To -> fffffffffftttttttttttttttt
+
+Arguments:
+
+ From - Supplies a pointer to the source buffer
+
+ FromSize - Supplies the size of the from buffer in bytes
+
+ To - Supplies a pointer to the destination buffer
+
+ ToSize - Supplies the size of the to buffer in bytes
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ LONG i;
+
+ //
+ // Shift over the To buffer enough to fit in the From buffer
+ //
+
+ for (i = ToSize - 1; i >= 0; i--) {
+
+ ((PUCHAR)To)[i+FromSize] = ((PUCHAR)To)[i];
+ }
+
+ //
+ // Now copy over the From buffer
+ //
+
+ for (i = 0; (ULONG)i < FromSize; i += 1) {
+
+ ((PUCHAR)To)[i] = ((PUCHAR)From)[i];
+
+ }
+
+ //
+ // and return to our caller
+ //
+
+ return;
+
+}
+
+
+//
+// Internal support routine
+//
+
+VOID
+DeleteData (
+ IN PVOID Data,
+ IN ULONG RemoveSize,
+ IN ULONG TotalSize
+ )
+
+/*++
+
+Routine Description:
+
+ This routine deletes a string of bytes from the front of a data buffer
+ and compresses the data. It also zeros out the part of the string
+ that is no longer in use. Pictorially the results are as follows
+
+ Before:
+
+ Data = DDDDDddddd
+ RemoveSize = 5
+ TotalSize = 10
+
+ After:
+
+ Data = ddddd00000
+
+Arguments:
+
+ Data - Supplies a pointer to the data being altered
+
+ RemoveSize - Supplies the number of bytes to delete from the front
+ of the data buffer
+
+ TotalSize - Supplies the total number of bytes in the data buffer
+ before the delete operation
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ ULONG i;
+
+ //
+ // Shift over the buffer to remove the amount
+ //
+
+ for (i = RemoveSize; i < TotalSize; i++) {
+
+ ((PUCHAR)Data)[i-RemoveSize] = ((PUCHAR)Data)[i];
+
+ }
+
+ //
+ // Now as a safety precaution we'll zero out the rest of the string
+ //
+
+ for (i = TotalSize - RemoveSize; i < TotalSize; i++) {
+
+ ((PUCHAR)Data)[i] = 0;
+ }
+
+ //
+ // And return to our caller
+ //
+
+ return;
+
+}
diff --git a/private/posix/psxss/apiinit.c b/private/posix/psxss/apiinit.c
new file mode 100644
index 000000000..0ec40a43d
--- /dev/null
+++ b/private/posix/psxss/apiinit.c
@@ -0,0 +1,152 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ apiinit.c
+
+Abstract:
+
+ This module contains the code to initialize the ApiPort of the POSIX
+ Emulation Subsystem.
+
+Author:
+
+ Steve Wood (stevewo) 22-Aug-1989
+
+Environment:
+
+ User Mode Only
+
+Revision History:
+
+ Ellen Aycock-Wright (ellena) 15-Jul-91 Modified for POSIX
+
+--*/
+
+#include "psxsrv.h"
+#include <ntcsrdll.h>
+#include <windef.h>
+#include <winbase.h>
+
+
+NTSTATUS
+PsxApiPortInitialize( VOID )
+{
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ ULONG i;
+ CHAR buf[SECURITY_DESCRIPTOR_MIN_LENGTH];
+ PSECURITY_DESCRIPTOR securityDescriptor;
+
+ RtlInitUnicodeString(&PsxApiPortName, PSX_SS_API_PORT_NAME);
+
+ securityDescriptor = (PSECURITY_DESCRIPTOR)buf;
+
+ Status = RtlCreateSecurityDescriptor(securityDescriptor,
+ SECURITY_DESCRIPTOR_REVISION);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = RtlSetDaclSecurityDescriptor(securityDescriptor, TRUE,
+ NULL, FALSE);
+
+ InitializeObjectAttributes(&ObjectAttributes, &PsxApiPortName, 0,
+ NULL, securityDescriptor);
+
+ IF_PSX_DEBUG(INIT) {
+ KdPrint(("PSXSS: Creating %wZ port and associated threads\n",
+ &PsxApiPortName));
+ }
+ Status = NtCreatePort(&PsxApiPort, &ObjectAttributes,
+ sizeof(PSX_API_CONNECTINFO), sizeof(PSX_API_MSG),
+ 4096 * 16);
+ ASSERT(NT_SUCCESS(Status));
+ if (!NT_SUCCESS(Status)) {
+ NtTerminateProcess(NtCurrentProcess(), 1);
+ }
+
+ for (i = 0; i < PsxNumberApiRequestThreads; i++) {
+ PsxServerThreadHandles[i + PSX_SS_FIRST_API_REQUEST_THREAD] =
+ CreateThread(NULL, (DWORD)0,
+ (LPTHREAD_START_ROUTINE)PsxApiRequestThread,
+ NULL, CREATE_SUSPENDED,
+ (LPDWORD)&PsxServerThreadClientIds[i +
+ PSX_SS_FIRST_API_REQUEST_THREAD]);
+ ASSERT(NULL != PsxServerThreadHandles[i +
+ PSX_SS_FIRST_API_REQUEST_THREAD]);
+ if (NULL == PsxServerThreadHandles[i +
+ PSX_SS_FIRST_API_REQUEST_THREAD]) {
+ NtTerminateProcess(NtCurrentProcess(), 1);
+ }
+ }
+
+
+ for (i = 0; i < PsxNumberApiRequestThreads; i++) {
+ Status = ResumeThread(PsxServerThreadHandles[i +
+ PSX_SS_FIRST_API_REQUEST_THREAD]);
+ ASSERT(-1 != Status);
+ if (-1 == Status) {
+ NtTerminateProcess(NtCurrentProcess(), 1);
+ }
+ }
+
+ //
+ // Create the Alarm-handling thread here, 'cuz there's no better
+ // place. See timer.c for AlarmThreadRoutine.
+ //
+
+ {
+ ULONG ThreadId;
+
+ AlarmThreadHandle = CreateThread(NULL, (DWORD)0,
+ (LPTHREAD_START_ROUTINE)AlarmThreadRoutine, NULL,
+ CREATE_SUSPENDED, &ThreadId);
+ if (NULL == AlarmThreadHandle) {
+ NtTerminateProcess(NtCurrentProcess(), 1);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = ResumeThread(AlarmThreadHandle);
+ ASSERT(-1 != Status);
+ }
+
+ return Status;
+}
+
+
+#if 0
+//
+// This thread is here sole to allow NTSD -p to attach to the Posix subsystem
+// process to debug it. It is required because all of the other thread in the
+// Posix subsystem server are waiting non-alertable in an LPC system servive.
+//
+
+NTSTATUS
+PsxDebugAttachThread (
+ IN PVOID Parameter
+ )
+{
+ LARGE_INTEGER TimeOut;
+ NTSTATUS Status;
+
+ // CsrIdentifyAlertableThread(); // So the debugger can interrupt us
+ while (TRUE) {
+ //
+ // Delay alertable for the longest possible integer relative to now.
+ //
+
+ TimeOut.LowPart = 0x0;
+ TimeOut.HighPart = 0x80000000;
+
+ Status = NtDelayExecution( TRUE, &TimeOut );
+
+ //
+ // Do this forever, so the debugger can attach whenever it wants.
+ //
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/private/posix/psxss/apilistn.c b/private/posix/psxss/apilistn.c
new file mode 100644
index 000000000..8e64c4784
--- /dev/null
+++ b/private/posix/psxss/apilistn.c
@@ -0,0 +1,192 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ apilistn.c
+
+Abstract:
+
+ This module implements the main loop servicing API RPC's.
+
+Author:
+
+ Mark Lucovsky (markl) 05-Apr-1989
+
+Revision History:
+
+--*/
+
+#include "psxsrv.h"
+#include <ntdbg.h>
+
+NTSTATUS
+PsxApiHandleConnectionRequest(
+ IN PPSX_API_MSG Message
+ )
+{
+ NTSTATUS Status;
+ REMOTE_PORT_VIEW ClientView;
+ BOOLEAN AcceptConnection;
+ PPSX_API_CONNECTINFO ConnectionInformation = &Message->ConnectionRequest;
+ HANDLE PortHandle;
+ PPSX_PROCESS Process;
+ BOOLEAN TerminateProcess;
+
+ AcceptConnection = TRUE;
+ TerminateProcess = FALSE;
+
+ Process = PsxLocateProcessByClientId(&Message->h.ClientId);
+
+ if (Process == NULL ||
+ Message->h.ClientViewSize > PSX_CLIENT_PORT_MEMORY_SIZE) {
+ AcceptConnection = FALSE;
+ goto accept;
+ }
+
+ ConnectionInformation->InitialPebPsxData =
+ Process->InitialPebPsxData;
+
+ //
+ // Now set up Directory Prefixes. There are two cases.
+ // Process->DirectoryPrefix is remote
+ // the process is connecting as a result of a fork.
+ // No action is needed.
+ // Process->DirectoryPrefix is local
+ // process is connecting as a result of an exec,
+ // or the process is connecting as a result of a new
+ // session. The local directory prefix's are
+ // propagated to the client
+ //
+
+ if (!IS_DIRECTORY_PREFIX_REMOTE(Process->DirectoryPrefix)) {
+
+ PSX_DIRECTORY_PREFIX ClientDirectoryPrefix;
+
+ //
+ // Get the client's directory prefix structure
+ // so we can figure out where he wants the strings
+ //
+
+ Status = NtReadVirtualMemory(Process->Process,
+ ConnectionInformation->DirectoryPrefix,
+ &ClientDirectoryPrefix,
+ sizeof(ClientDirectoryPrefix), NULL);
+
+ if (!NT_SUCCESS(Status)) {
+ TerminateProcess = TRUE;
+ goto accept;
+ }
+
+ //
+ // Make sure the buffer lengths are in sync.
+ //
+
+ if (ClientDirectoryPrefix.NtCurrentWorkingDirectory.MaximumLength
+ < Process->DirectoryPrefix->NtCurrentWorkingDirectory.Length ||
+ ClientDirectoryPrefix.PsxRoot.MaximumLength <
+ Process->DirectoryPrefix->PsxRoot.Length) {
+
+ TerminateProcess = TRUE;
+ goto accept;
+ }
+
+ //
+ // Push the pathname prefixes out
+ //
+
+ Status = NtWriteVirtualMemory(Process->Process,
+ ClientDirectoryPrefix.NtCurrentWorkingDirectory.Buffer,
+ Process->DirectoryPrefix->NtCurrentWorkingDirectory.Buffer,
+ Process->DirectoryPrefix->NtCurrentWorkingDirectory.Length,
+ NULL);
+
+ if (!NT_SUCCESS(Status)) {
+ TerminateProcess = TRUE;
+ goto accept;
+ }
+
+ ClientDirectoryPrefix.NtCurrentWorkingDirectory.Length =
+ Process->DirectoryPrefix->NtCurrentWorkingDirectory.Length;
+
+ Status = NtWriteVirtualMemory(Process->Process,
+ ClientDirectoryPrefix.PsxRoot.Buffer,
+ Process->DirectoryPrefix->PsxRoot.Buffer,
+ Process->DirectoryPrefix->PsxRoot.Length, NULL);
+
+ if (!NT_SUCCESS(Status)) {
+ TerminateProcess = TRUE;
+ goto accept;
+ }
+
+ ClientDirectoryPrefix.PsxRoot.Length =
+ Process->DirectoryPrefix->PsxRoot.Length;
+
+ //
+ // Now Push out the modified version of the client's
+ // directory prefix. We set the length to 0 so getcwd()
+ // will know to recomput the Posix version of the directory.
+ //
+
+ ClientDirectoryPrefix.PsxCurrentWorkingDirectory.Length = 0;
+
+ Status = NtWriteVirtualMemory(Process->Process,
+ ConnectionInformation->DirectoryPrefix,
+ &ClientDirectoryPrefix,
+ sizeof(ClientDirectoryPrefix), NULL);
+
+ if (!NT_SUCCESS(Status)) {
+ TerminateProcess = TRUE;
+ goto accept;
+ }
+
+ //
+ // Deallocate the local directory prefix
+ //
+
+ RtlFreeHeap(PsxHeap, 0, Process->DirectoryPrefix->NtCurrentWorkingDirectory.Buffer);
+ RtlFreeHeap(PsxHeap, 0, Process->DirectoryPrefix->PsxRoot.Buffer);
+ RtlFreeHeap(PsxHeap, 0, Process->DirectoryPrefix);
+
+ Process->DirectoryPrefix = MAKE_DIRECTORY_PREFIX_REMOTE(ConnectionInformation->DirectoryPrefix);
+ }
+
+accept:
+ ClientView.Length = sizeof(ClientView);
+ ClientView.ViewSize = 0;
+ ClientView.ViewBase = 0;
+
+ if (!AcceptConnection) {
+ KdPrint(( "PSXSS: Refusing connection from client %x.%x (Process %x)\n",
+ Message->h.ClientId.UniqueProcess,
+ Message->h.ClientId.UniqueThread,
+ Process
+ ));
+ }
+
+ Status = NtAcceptConnectPort(&PortHandle, (PVOID)Process,
+ (PPORT_MESSAGE) Message, AcceptConnection,
+ NULL, &ClientView);
+
+ if (NT_SUCCESS(Status) && AcceptConnection) {
+ Process->SignalDeliverer =
+ ConnectionInformation->SignalDeliverer;
+ Process->NullApiCaller = ConnectionInformation->NullApiCaller;
+ Process->State = Active;
+
+ Process->ClientPort = PortHandle;
+ Process->ClientViewBase = (PCH)ClientView.ViewBase;
+ Process->ClientViewBounds = (PCH)ClientView.ViewBase +
+ ClientView.ViewSize;
+ Status = NtCompleteConnectPort(PortHandle);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: NtCompleteConnectPort: 0x%x\n", Status));
+ }
+ if (TerminateProcess) {
+ PsxSignalProcess(Process,SIGKILL);
+ }
+ }
+
+ return Status;
+}
diff --git a/private/posix/psxss/apireqst.c b/private/posix/psxss/apireqst.c
new file mode 100644
index 000000000..fd371211c
--- /dev/null
+++ b/private/posix/psxss/apireqst.c
@@ -0,0 +1,508 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ apireqst.c
+
+Abstract:
+
+ This module implements the main loop servicing API RPC's.
+
+Author:
+
+ Mark Lucovsky (markl) 05-Apr-1989
+
+Revision History:
+
+--*/
+
+#include "psxsrv.h"
+#include "ntdbg.h"
+
+//
+// API Dispatch Table
+//
+
+PPSX_API_ROUTINE PsxServerApiDispatch[PsxMaxApiNumber] = {
+ PsxFork,
+ PsxExec,
+ PsxWaitPid,
+ PsxExit,
+ PsxKill,
+ PsxSigAction,
+ PsxSigProcMask,
+ PsxSigPending,
+ PsxSigSuspend,
+ PsxAlarm,
+ PsxGetIds,
+ PsxSetUid,
+ PsxSetGid,
+ PsxGetGroups,
+ PsxGetLogin,
+ PsxCUserId,
+ PsxSetSid,
+ PsxSetPGroupId,
+ PsxUname,
+ PsxTime,
+ PsxGetProcessTimes,
+ PsxTtyName,
+ PsxIsatty,
+ PsxSysconf,
+ PsxOpen,
+ PsxUmask,
+ PsxLink,
+ PsxMkDir,
+ PsxMkFifo,
+ PsxRmDir,
+ PsxRename,
+ PsxStat,
+ PsxFStat,
+ PsxAccess,
+ PsxChmod,
+ PsxChown,
+ PsxUtime,
+ PsxPathConf,
+ PsxFPathConf,
+ PsxPipe,
+ PsxDup,
+ PsxDup2,
+ PsxClose,
+ PsxRead,
+ PsxWrite,
+ PsxFcntl,
+ PsxLseek,
+ PsxTcGetAttr,
+ PsxTcSetAttr,
+ PsxTcSendBreak,
+ PsxTcDrain,
+ PsxTcFlush,
+ PsxTcFlow,
+ PsxTcGetPGrp,
+ PsxTcSetPGrp,
+ PsxGetPwUid,
+ PsxGetPwNam,
+ PsxGetGrGid,
+ PsxGetGrNam,
+ PsxUnlink,
+ PsxReadDir,
+ PsxFtruncate,
+ PsxNull
+
+#ifdef PSX_SOCKET
+ ,
+ PsxSocket,
+ PsxAccept,
+ PsxBind,
+ PsxConnect,
+ PsxGetPeerName,
+ PsxGetSockName,
+ PsxGetSockOpt,
+ PsxListen,
+ PsxRecv,
+ PsxSend,
+ PsxSendTo,
+ PsxSetSockOpt,
+ PsxShutdown
+
+#endif // PSX_SOCKET
+
+ };
+
+#if DBG
+
+PSZ PsxServerApiName[PsxMaxApiNumber] = {
+ "PsxFork",
+ "PsxExec",
+ "PsxWaitPid",
+ "PsxExit",
+ "PsxKill",
+ "PsxSigAction",
+ "PsxSigProcMask",
+ "PsxSigPending",
+ "PsxSigSuspend",
+ "PsxAlarm",
+ "PsxGetIds",
+ "PsxSetUid",
+ "PsxSetGid",
+ "PsxGetGroups",
+ "PsxGetLogin",
+ "PsxCUserId",
+ "PsxSetSid",
+ "PsxSetPGroupId",
+ "PsxUname",
+ "PsxTime",
+ "PsxGetProcessTimes",
+ "PsxTtyName",
+ "PsxIsatty",
+ "PsxSysconf",
+ "PsxOpen",
+ "PsxUmask",
+ "PsxLink",
+ "PsxMkDir",
+ "PsxMkFifo",
+ "PsxRmDir",
+ "PsxRename",
+ "PsxStat",
+ "PsxFStat",
+ "PsxAccess",
+ "PsxChmod",
+ "PsxChown",
+ "PsxUtime",
+ "PsxPathConf",
+ "PsxFPathConf",
+ "PsxPipe",
+ "PsxDup",
+ "PsxDup2",
+ "PsxClose",
+ "PsxRead",
+ "PsxWrite",
+ "PsxFcntl",
+ "PsxLseek",
+ "PsxTcGetAttr",
+ "PsxTcSetAttr",
+ "PsxTcSendBreak",
+ "PsxTcDrain",
+ "PsxTcFlush",
+ "PsxTcFlow",
+ "PsxTcGetPGrp",
+ "PsxTcSetPGrp",
+ "PsxGetPwUid",
+ "PsxGetPwNam",
+ "PsxGetGrGid",
+ "PsxGetGrNam",
+ "PsxUnlink",
+ "PsxReadDir",
+ "PsxFtruncate",
+ "PsxNull"
+
+#ifdef PSX_SOCKET
+ ,
+ "PsxSocket",
+ "PsxAccept",
+ "PsxBind",
+ "PsxConnect",
+ "PsxGetPeerName",
+ "PsxGetSockName",
+ "PsxGetSockOpt",
+ "PsxListen",
+ "PsxRecv",
+ "PsxRecvFrom",
+ "PsxSend",
+ "PsxSendTo",
+ "PsxSetSockOpt",
+ "PsxShutdown"
+
+#endif // PSX_SOCKET
+
+ };
+
+VOID dumpmsg( IN PPSX_API_MSG ReplyMsg);
+
+#endif //DBG
+
+NTSTATUS
+PsxApiRequestThread(
+ IN PVOID Parameter
+ )
+{
+ NTSTATUS Status;
+ PPSX_PROCESS Process;
+ PSX_API_MSG ReceiveMsg;
+ PPSX_API_MSG ReplyMsg;
+ USHORT MessageType;
+ PVOID PortContext; // pointer to process structure
+
+ KERNEL_USER_TIMES ThreadTime;
+ ULONG PosixUTime1, PosixUTime2, Remainder, LengthNeeded;
+ ULONG PosixSTime1, PosixSTime2;
+ BOOLEAN Reply;
+
+ UNREFERENCED_PARAMETER(Parameter);
+
+ ReplyMsg = NULL;
+ while (TRUE) {
+ Status = NtQueryInformationThread(NtCurrentThread(),
+ ThreadTimes,
+ (PVOID)&ThreadTime, sizeof(ThreadTime), &LengthNeeded);
+ ASSERT(NT_SUCCESS(Status));
+
+ PosixUTime1 = RtlExtendedLargeIntegerDivide(
+ ThreadTime.UserTime, 10000, &Remainder).LowPart;
+ PosixSTime1 = RtlExtendedLargeIntegerDivide(
+ ThreadTime.KernelTime, 10000, &Remainder).LowPart;
+
+ Status = NtReplyWaitReceivePort(PsxApiPort,
+ &PortContext, (PPORT_MESSAGE)ReplyMsg,
+ (PPORT_MESSAGE)&ReceiveMsg);
+ if (Status != 0) {
+ if (NT_SUCCESS(Status)) {
+ continue;
+ }
+ if (STATUS_INVALID_CID == Status) {
+ ReplyMsg = NULL;
+ continue;
+ }
+ KdPrint(("PSXSS: ReceivePort failed: 0x%x\n",
+ Status));
+ ReplyMsg = NULL;
+ continue;
+ }
+
+ MessageType = ReceiveMsg.h.u2.s2.Type;
+
+ if (MessageType == LPC_CONNECTION_REQUEST) {
+ PsxApiHandleConnectionRequest( &ReceiveMsg );
+ ReplyMsg = NULL;
+ continue;
+ }
+
+ Process = PsxLocateProcessByClientId(&ReceiveMsg.h.ClientId);
+ if (NULL == Process) {
+ if (LPC_CLIENT_DIED == MessageType ||
+ LPC_PORT_CLOSED == MessageType ||
+ LPC_ERROR_EVENT == MessageType
+ ) {
+ ReplyMsg = NULL;
+ continue;
+ }
+ if (LPC_EXCEPTION == MessageType) {
+ ReplyMsg = &ReceiveMsg;
+ ReplyMsg->ReturnValue = DBG_CONTINUE;
+ continue;
+ }
+
+ KdPrint(("PSXSS: msg %d from unknown client: %d, %d\n",
+ ReceiveMsg.ApiNumber,
+ ReceiveMsg.h.ClientId.UniqueProcess,
+ ReceiveMsg.h.ClientId.UniqueThread));
+ ReceiveMsg.Error = ESRCH;
+ ReplyMsg = NULL;
+ continue;
+ }
+
+
+ //
+ // For each POSIX API message
+ // - Validate the API Number
+ // - Dispatch the call
+ // - Optionally Reply
+ //
+
+ if (LPC_CLIENT_DIED == MessageType) {
+ // XXX.mjb: do we need to increment InPsx here?
+ // XXX.mjb: this exit status should be meaningful.
+
+ Exit(Process, (ULONG)-1);
+ ReplyMsg = NULL; // no one to reply to.
+ continue;
+ }
+ if (LPC_EXCEPTION == MessageType) {
+ PDBGKM_APIMSG m;
+
+ KdPrint(("PSXSS: Pid 0x%x has taken an exception\n",
+ Process->Pid));
+
+ Exit(Process, (ULONG)-1);
+
+ //
+ // XXX.mjb: would be nice to print an error
+ // message.
+ //
+
+ m = (PDBGKM_APIMSG)&ReceiveMsg;
+ m->ReturnedStatus = DBG_CONTINUE;
+ ReplyMsg = &ReceiveMsg;
+ ReplyMsg->ReturnValue = DBG_CONTINUE;
+ continue;
+ }
+ if (LPC_ERROR_EVENT == MessageType) {
+ PHARDERROR_MSG m;
+
+ m = (PHARDERROR_MSG)&ReceiveMsg;
+ m->Response = (ULONG)ResponseNotHandled;
+
+ Exit(Process, (ULONG)-1);
+ ReplyMsg = NULL; // no one to reply to.
+ continue;
+ }
+ if (LPC_REQUEST != MessageType) {
+ KdPrint(("PSXSS: Unknown message type 0x%x\n",
+ MessageType));
+ ReplyMsg = NULL; // no one to reply to.
+ continue;
+ }
+
+ if (Process != PortContext) {
+ //
+ // This message was sent by a diffferent process than
+ // the one that now has this ClientId. We discard
+ // the message.
+ //
+
+ ReplyMsg = NULL;
+ continue;
+ }
+
+ AcquireProcessLock(Process);
+
+ Process->InPsx++;
+ Process->IntControlBlock = (PINTCB) NULL;
+
+ ReleaseProcessLock(Process);
+
+ if (ReceiveMsg.ApiNumber >= PsxMaxApiNumber) {
+ KdPrint(("PSXSS: %lx is invalid ApiNumber\n",
+ ReceiveMsg.ApiNumber));
+ ReceiveMsg.Error = ENOSYS;
+ ReplyMsg = &ReceiveMsg;
+ continue;
+ }
+
+ ReplyMsg = &ReceiveMsg;
+
+ ReceiveMsg.Error = 0L;
+ ReceiveMsg.ReturnValue = 0L;
+
+
+ Reply = (*PsxServerApiDispatch[ReceiveMsg.ApiNumber])
+ (Process, &ReceiveMsg);
+
+ if (!(Process->Flags & P_FREE)) {
+
+ //
+ // the process has exited, don't try to fiddle
+ // with times.
+ //
+
+ if (!Reply) {
+ ReplyMsg = NULL;
+ continue;
+ }
+ }
+
+ //
+ // the user and system time for the posix server are added to
+ // the process *system* time (the system is executing in user
+ // and kernel mode on behalf of the process).
+ //
+
+ Status = NtQueryInformationThread(NtCurrentThread(),
+ ThreadTimes,
+ (PVOID)&ThreadTime, sizeof(ThreadTime), &LengthNeeded);
+ ASSERT(NT_SUCCESS(Status));
+ PosixUTime2 = RtlExtendedLargeIntegerDivide(
+ ThreadTime.UserTime, 10000, &Remainder).LowPart;
+ PosixSTime2 = RtlExtendedLargeIntegerDivide(
+ ThreadTime.KernelTime, 10000, &Remainder).LowPart;
+
+ Process->ProcessTimes.tms_stime += (PosixUTime2 - PosixUTime1);
+ Process->ProcessTimes.tms_stime += (PosixSTime2 - PosixSTime1);
+
+ if (!Reply) {
+ ReplyMsg = NULL;
+ continue;
+ }
+
+ if (PendingSignalHandledInside(Process, &ReceiveMsg, NULL)) {
+ ReplyMsg = NULL; // Don't reply
+ continue;
+ }
+
+ AcquireProcessLock(Process);
+ --Process->InPsx;
+ ReleaseProcessLock(Process);
+ }
+ NtTerminateThread(NtCurrentThread(), Status);
+ return Status;
+}
+
+
+VOID
+ApiReply(
+ IN PPSX_PROCESS Process,
+ IN PPSX_API_MSG ReplyMsg,
+ IN sigset_t *RestoreBlockSigset OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This routine issues a reply for the specified message on behalf of the
+ specified process.
+
+Arguments:
+
+ Process - Supplies the address of the process on whose behalf the reply
+ is being made.
+
+ ReplyMsg - Supplies the value of the reply message.
+
+ RestoreBlockSigset - Supplies an optional blocked signal mask that
+ should be restored after the signal completes
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ HANDLE ReplyPort;
+
+ if (!PendingSignalHandledInside(Process, ReplyMsg, RestoreBlockSigset)) {
+
+#if DBG
+ IF_PSX_DEBUG( MSGDUMP ) {
+ KdPrint(("--- REPLY TebServicer %lx Message... Pid %lx\n",
+ NtCurrentTeb(), Process->Pid));
+ dumpmsg(ReplyMsg);
+ }
+#endif //DBG
+
+ ReplyPort = Process->ClientPort;
+
+ Status = NtReplyPort(ReplyPort, (PPORT_MESSAGE)ReplyMsg);
+ if (!NT_SUCCESS(Status)) {
+ //
+ // We can get here, if, for instance, somebody shoots a
+ // process out from under us.
+ //
+ KdPrint(("PSXSS: ReplyPort: 0x%x\n", Status));
+ }
+
+ --Process->InPsx;
+ return;
+ }
+}
+
+#if DBG
+
+VOID
+dumpmsg(
+ IN PPSX_API_MSG Msg
+ )
+{
+ PULONG l;
+ ULONG p1,p2,p3;
+
+ KdPrint(("Length %lx\n",Msg->h.u1.Length));
+ KdPrint(("MapInfo and Type %lx\n",Msg->h.u2.ZeroInit));
+ KdPrint(("ClientId %lx.%lx\n",
+ Msg->h.ClientId.UniqueProcess,
+ Msg->h.ClientId.UniqueThread));
+ KdPrint(("ApiName %lx %s\n",Msg->ApiNumber,PsxServerApiName[Msg->ApiNumber]));
+ KdPrint(("Error %lx\n",Msg->Error));
+ KdPrint(("ReturnValue %lx\n",Msg->ReturnValue));
+
+ l = (PULONG) (&Msg->u.Fork);
+ p1 = *l++;
+ p2 = *l++;
+ p3 = *l++;
+ KdPrint(("Args[0..2] \t%lx\n\t\t%lx\n\t\t%lx\n",p1,p2,p3));
+}
+
+#endif //DBG
diff --git a/private/posix/psxss/concreat.c b/private/posix/psxss/concreat.c
new file mode 100644
index 000000000..035f5ef8f
--- /dev/null
+++ b/private/posix/psxss/concreat.c
@@ -0,0 +1,276 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ concreat.c
+
+Abstract:
+
+ This module handles the request to create a session for PSXSES.
+
+Author:
+
+ Avi Nathan (avin) 17-Jul-1991
+
+Revision History:
+
+ Ellen Aycock-Wright (ellena) 15-Sept-1991 Modified for POSIX
+
+--*/
+
+#include <stdio.h>
+#include "psxsrv.h"
+
+#define NTPSX_ONLY
+#include "sesport.h"
+
+VOID
+SetDefaultLibPath(
+ OUT PANSI_STRING LibPath,
+ IN PCHAR EnvStrings
+ )
+{
+ PCHAR p;
+
+ // BUGBUG: Unicode
+ for (p = EnvStrings; '\0' != *p; p += strlen(p) + 1) {
+ if (0 == _strnicmp(p, "_PSXLIBPATH=",
+ sizeof("_PSXLIBPATH=") - 1)) {
+ p += sizeof("_PSXLIBPATH=") - 1;
+ break;
+ }
+ }
+ RtlInitAnsiString(LibPath, p);
+}
+
+//
+// PsxCreateConSession -- Create a new Posix session, with a process
+// to run in it. The new process inherits security info and quota
+// stuff from the Posix session manager, which is identified in the
+// RequestMsg that's passed in.
+//
+NTSTATUS
+PsxCreateConSession(
+ IN OUT PVOID RequestMsg
+ )
+{
+ NTSTATUS Status;
+ PSCREQ_CREATE Create = &((PPSXSESREQUESTMSG)RequestMsg)->d.Create;
+ PPSX_PROCESS NewProcess;
+ PCHAR BaseAddress;
+ PSX_EXEC_INFO ExecInfo;
+ HANDLE SectionHandle = NULL;
+ HANDLE ParentProc; // a handle on the psxses process
+ OBJECT_ATTRIBUTES Obj; // used for opening psxses process
+ PPSX_SESSION pS;
+ PPSX_CONTROLLING_TTY Terminal;
+ BOOLEAN PsxCreateOk;
+ int i;
+
+ Terminal = GetConnectingTerminal(Create->SessionUniqueId);
+ if (NULL == Terminal) {
+ KdPrint(("PSXSS: Connect by session not connecting\n"));
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ BaseAddress = PsxViewSessionData(Create->SessionUniqueId,
+ &SectionHandle);
+ if (NULL == BaseAddress) {
+ RtlDeleteCriticalSection(&Terminal->Lock);
+ NtClose(Terminal->ConsoleCommPort);
+ NtClose(Terminal->ConsolePort);
+
+ RtlFreeHeap(PsxHeap, 0, Terminal);
+
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ pS = PsxAllocateSession(Terminal, 0);
+ pS->Terminal->ConsolePort = Create->SessionPort;
+ pS->Terminal->IoBuffer = BaseAddress;
+
+ RtlInitAnsiString(&ExecInfo.Path, BaseAddress + Create->PgmNameOffset);
+
+ RtlInitAnsiString(&ExecInfo.CWD,
+ BaseAddress + Create->CurrentDirOffset);
+
+ ExecInfo.Argv.Buffer = BaseAddress + Create->ArgsOffset;
+ ExecInfo.Argv.Length = 0x4000; // XXX.mjb: use real length
+ ExecInfo.Argv.MaximumLength = 0x4000; // XXX.mjb: use real length
+
+ SetDefaultLibPath(&ExecInfo.LibPath,
+ BaseAddress + Create->EnvironmentOffset);
+
+ InitializeObjectAttributes(&Obj, NULL, 0, NULL, NULL);
+
+ Status = NtOpenProcess(&ParentProc, PROCESS_CREATE_PROCESS,
+ &Obj, &((PPORT_MESSAGE)RequestMsg)->ClientId);
+ if (!NT_SUCCESS(Status)) {
+ RtlEnterCriticalSection(&PsxNtSessionLock);
+ PsxDeallocateSession(pS);
+
+ if (NULL != SectionHandle) {
+ NtClose(SectionHandle);
+ }
+ KdPrint(("PSXSS: NtOpenProcess: 0x%x\n", Status));
+ return Status;
+ }
+
+ //
+ // create a process for the new session.
+ //
+
+ Status = NtImpersonateClientOfPort(pS->Terminal->ConsoleCommPort,
+ RequestMsg);
+ ASSERT(NT_SUCCESS(Status));
+
+ PsxCreateOk = PsxCreateProcess(&ExecInfo, &NewProcess,
+ ParentProc, pS);
+
+ EndImpersonation();
+
+ NtClose(ParentProc);
+
+ if (!PsxCreateOk) {
+ //
+ // Must hold PsxNtSessionLock when calling DeallocSession.
+ //
+ RtlEnterCriticalSection(&PsxNtSessionLock);
+ PsxDeallocateSession(pS);
+ if (NULL != SectionHandle) {
+ NtClose(SectionHandle);
+ }
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ pS->Terminal->ForegroundProcessGroup = NewProcess->ProcessGroupId;
+
+
+ //
+ // Open file descriptors for stdin, stdout, and stderr.
+ //
+
+ for (i = 0; i < Create->OpenFiles; ++i) {
+ ULONG fd, Error = 0;
+ PFILEDESCRIPTOR Fd;
+
+ Fd = AllocateFd(NewProcess, 0, &fd);
+ ASSERT(NULL != Fd);
+
+ Error += OpenTty(NewProcess, Fd,
+ FILE_READ_DATA|FILE_WRITE_DATA, 0, FALSE);
+ Fd->SystemOpenFileDesc->NtIoHandle = (HANDLE)i;
+
+ ASSERT(0 == Error);
+ }
+
+ if (NULL != SectionHandle) {
+ NtClose(SectionHandle);
+ }
+ Status = NtResumeThread(NewProcess->Thread, NULL);
+ ASSERT(NT_SUCCESS(Status));
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+PsxTerminateConSession(
+ IN PPSX_SESSION Session,
+ IN ULONG ExitStatus
+ )
+{
+ NTSTATUS Status;
+ SCREQUESTMSG Request;
+
+ if (NULL == Session || NULL == Session->Terminal) {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+
+ Request.Request = TaskManRequest;
+ Request.d.Tm.Request = TmExit;
+ Request.d.Tm.ExitStatus = ExitStatus;
+
+ PORT_MSG_TOTAL_LENGTH(Request) = sizeof(SCREQUESTMSG);
+ PORT_MSG_DATA_LENGTH(Request) = sizeof(SCREQUESTMSG) - sizeof(PORT_MESSAGE);
+ PORT_MSG_ZERO_INIT(Request) = 0L;
+
+ if (NULL == Session->Terminal)
+ return STATUS_UNSUCCESSFUL;
+
+ Status = NtRequestPort(Session->Terminal->ConsolePort,
+ (PPORT_MESSAGE)&Request);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: Unable to send terminate request: Status == %X\n",
+ Status));
+ }
+
+ Status = NtClose(Session->Terminal->ConsolePort);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: Close ConsolePort: 0x%x\n", Status));
+ }
+ Status = NtClose(Session->Terminal->ConsoleCommPort);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: Close ConsoleCommPort: 0x%x\n", Status));
+ }
+ return STATUS_SUCCESS;
+}
+
+PVOID
+PsxViewSessionData(
+ IN ULONG SessionId,
+ OUT PHANDLE RetSection
+ )
+{
+ CHAR SessionName[PSX_SES_BASE_PORT_NAME_LENGTH];
+ STRING SessionDataName;
+ UNICODE_STRING SessionDataName_U;
+
+ NTSTATUS Status;
+ HANDLE SectionHandle;
+ ULONG ViewSize = 0L;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ PVOID SessionDataBaseAddress;
+
+ PSX_GET_SESSION_DATA_NAME(SessionName, SessionId);
+
+ RtlInitAnsiString(&SessionDataName, SessionName);
+ Status = RtlAnsiStringToUnicodeString(&SessionDataName_U, &SessionDataName,
+ TRUE);
+ if (!NT_SUCCESS(Status)) {
+ return NULL;
+ }
+
+ InitializeObjectAttributes(&ObjectAttributes, &SessionDataName_U, 0,
+ NULL, NULL);
+
+ Status = NtOpenSection(&SectionHandle, SECTION_MAP_WRITE,
+ &ObjectAttributes);
+
+ RtlFreeUnicodeString(&SessionDataName_U);
+
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: NtOpenSection: 0x%x\n", Status));
+ return NULL;
+ }
+
+ //
+ // Let MM locate the view
+ //
+
+ SessionDataBaseAddress = 0;
+
+ Status = NtMapViewOfSection(SectionHandle, NtCurrentProcess(),
+ &SessionDataBaseAddress, 0L, 0L, NULL,
+ &ViewSize, ViewUnmap, 0L, PAGE_READWRITE);
+
+ if (!NT_SUCCESS(Status)) {
+ NtClose(SectionHandle);
+ return NULL;
+ }
+
+ *RetSection = SectionHandle;
+ return SessionDataBaseAddress;
+}
diff --git a/private/posix/psxss/coninit.c b/private/posix/psxss/coninit.c
new file mode 100644
index 000000000..1e3c2632b
--- /dev/null
+++ b/private/posix/psxss/coninit.c
@@ -0,0 +1,123 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ coninit.c
+
+Abstract:
+
+ This module contains the code to initialize the Console Port of the POSIX
+ Emulation Subsystem.
+
+Author:
+
+ Avi Nathan (avin) 17-Jul-1991
+
+Environment:
+
+ User Mode Only
+
+Revision History:
+
+--*/
+
+#include "psxsrv.h"
+#include <windows.h>
+
+#define NTPSX_ONLY
+#include "sesport.h"
+
+NTSTATUS
+PsxInitializeConsolePort(
+ VOID
+ )
+{
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ STRING PsxSessionPortName;
+ UNICODE_STRING PsxSessionPortName_U;
+ HANDLE PsxSessionDirectory;
+ CHAR localSecurityDescriptor[SECURITY_DESCRIPTOR_MIN_LENGTH];
+ PSECURITY_DESCRIPTOR
+ securityDescriptor = (PVOID)localSecurityDescriptor;
+
+ //
+ // Create a directory in the object name space for the session port
+ // names
+ //
+
+ RtlInitAnsiString(&PsxSessionPortName, PSX_SES_BASE_PORT_NAME);
+
+ Status = RtlAnsiStringToUnicodeString(&PsxSessionPortName_U,
+ &PsxSessionPortName, TRUE);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = RtlCreateSecurityDescriptor(securityDescriptor,
+ SECURITY_DESCRIPTOR_REVISION);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = RtlSetDaclSecurityDescriptor(securityDescriptor,
+ TRUE, NULL, FALSE);
+
+ InitializeObjectAttributes(&ObjectAttributes, &PsxSessionPortName_U,
+ 0, NULL, securityDescriptor);
+
+ Status = NtCreateDirectoryObject(&PsxSessionDirectory,
+ DIRECTORY_ALL_ACCESS, &ObjectAttributes);
+
+ RtlFreeUnicodeString(&PsxSessionPortName_U);
+
+ ASSERT(NT_SUCCESS(Status));
+
+ RtlInitUnicodeString(&PsxSessionPortName_U, PSX_SS_SESSION_PORT_NAME);
+
+ IF_PSX_DEBUG(LPC) {
+ KdPrint(("PSXSS: Creating %wZ port and associated thread\n",
+ &PsxSessionPortName_U ));
+ }
+
+ Status = RtlCreateSecurityDescriptor(securityDescriptor,
+ SECURITY_DESCRIPTOR_REVISION);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = RtlSetDaclSecurityDescriptor(securityDescriptor,
+ TRUE, NULL, FALSE);
+
+ InitializeObjectAttributes(&ObjectAttributes, &PsxSessionPortName_U,
+ 0, NULL, securityDescriptor);
+
+ Status = NtCreatePort(&PsxSessionPort, &ObjectAttributes,
+ sizeof(PSXSESCONNECTINFO), sizeof(PSXSESREQUESTMSG),
+ sizeof( PSXSESREQUESTMSG) * 32);
+ ASSERT(NT_SUCCESS(Status));
+#if BOGUS_THREADS
+ Status = RtlCreateUserThread(NtCurrentProcess(), NULL, TRUE, 0, 0, 0,
+ PsxSessionRequestThread, NULL,
+ &PsxSessionRequestThreadHandle, NULL);
+ ASSERT(NT_SUCCESS(Status));
+#else
+ {
+ DWORD Id;
+ PsxSessionRequestThreadHandle = CreateThread(
+ NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)PsxSessionRequestThread,
+ NULL,
+ CREATE_SUSPENDED,
+ &Id
+ );
+ }
+#endif
+
+ //
+ // BUGBUG: this guy is going to spin for quite a while until
+ // he does something
+ //
+
+ Status = NtResumeThread(PsxSessionRequestThreadHandle, NULL);
+ ASSERT(NT_SUCCESS(Status));
+
+ return Status;
+}
diff --git a/private/posix/psxss/conio.c b/private/posix/psxss/conio.c
new file mode 100644
index 000000000..6efb51b36
--- /dev/null
+++ b/private/posix/psxss/conio.c
@@ -0,0 +1,502 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ conio.c
+
+Abstract:
+
+ This module implements server performed console io
+
+Author:
+
+ Matthew Bradburn (mattbr) 18-Dec-1992
+
+Revision History:
+
+--*/
+
+
+#include <sys/stat.h>
+#include "psxsrv.h"
+#define NTPSX_ONLY
+#include "sesport.h"
+
+BOOLEAN
+ConOpen(
+ IN PPSX_PROCESS p,
+ IN PFILEDESCRIPTOR Fd,
+ IN OUT PPSX_API_MSG m
+ );
+
+BOOLEAN
+ConRead(
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd
+ );
+
+BOOLEAN
+ConWrite (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd
+ );
+
+BOOLEAN
+ConDup (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd,
+ IN PFILEDESCRIPTOR FdDup
+ );
+
+BOOLEAN
+ConLseek (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd
+ );
+
+BOOLEAN
+ConStat (
+ IN PIONODE IoNode,
+ IN HANDLE FileHandle,
+ OUT struct stat *StatBuf,
+ OUT NTSTATUS *Status
+ );
+
+VOID
+ConLastClose (
+ IN PPSX_PROCESS p,
+ IN PSYSTEMOPENFILE SystemOpenFile
+ )
+
+{
+ NTSTATUS st;
+ SCREQUESTMSG Request;
+
+#if 0
+//
+// We don't do this anymore, because if the session manager has
+// died, we'll never get a reply, and we'll hang while holding some
+// lock or another.
+//
+
+ Request.Request = ConRequest;
+ Request.d.Con.Request = ScCloseFile;
+ Request.d.Con.d.IoBuf.Handle = SystemOpenFile->NtIoHandle;
+
+ PORT_MSG_TOTAL_LENGTH(Request) = sizeof(SCREQUESTMSG);
+ PORT_MSG_DATA_LENGTH(Request) = sizeof(SCREQUESTMSG) -
+ sizeof(PORT_MESSAGE);
+ PORT_MSG_ZERO_INIT(Request) = 0;
+
+ RtlEnterCriticalSection(&SystemOpenFile->Terminal->Lock);
+
+ st = NtRequestWaitReplyPort(
+ SystemOpenFile->Terminal->ConsolePort,
+ (PPORT_MESSAGE)&Request, (PPORT_MESSAGE)&Request);
+ // ASSERT(NT_SUCCESS(st));
+
+ RtlLeaveCriticalSection(&SystemOpenFile->Terminal->Lock);
+#endif
+
+ SystemOpenFile->NtIoHandle = NULL;
+}
+
+
+PSXIO_VECTORS ConVectors = {
+ ConOpen,
+ NULL,
+ NULL,
+ ConLastClose,
+ NULL,
+ ConRead,
+ ConWrite,
+ ConDup,
+ ConLseek,
+ ConStat
+ };
+
+
+BOOLEAN
+ConOpen(
+ IN PPSX_PROCESS p,
+ IN PFILEDESCRIPTOR Fd,
+ IN OUT PPSX_API_MSG m
+ )
+/*++
+
+Routine Description:
+
+ This routine is called when the path /dev/tty is opened. Its
+ function is to set up the stuff for stat that doesn't exist because
+ there isn't really such a file.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the request.
+
+ Fd - supplies the address of the file descriptor being written.
+
+Return Value:
+
+ FALSE - Failure.
+ TRUE - Success.
+
+--*/
+{
+ PPSX_OPEN_MSG args;
+ LARGE_INTEGER time;
+ ULONG posix_time;
+
+ args = &m->u.Open;
+
+ NtQuerySystemTime(&time);
+ if (!RtlTimeToSecondsSince1970(&time, &posix_time)) {
+ posix_time = 0;
+ }
+
+ RtlEnterCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
+ Fd->SystemOpenFileDesc->IoNode->ModifyDataTime = posix_time;
+ Fd->SystemOpenFileDesc->IoNode->ModifyIoNodeTime = posix_time;
+ Fd->SystemOpenFileDesc->IoNode->AccessDataTime = posix_time;
+ RtlLeaveCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
+
+ return TRUE;
+}
+
+
+BOOLEAN
+ConWrite (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements write when the device being written
+ is a file.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the request.
+
+ Fd - supplies the address of the file descriptor being written.
+
+Return Value:
+
+--*/
+
+{
+ PPSX_WRITE_MSG args;
+ NTSTATUS st;
+ IO_STATUS_BLOCK Iosb;
+ LARGE_INTEGER ByteOffset;
+ ULONG IoBufferSize;
+ FILE_FS_SIZE_INFORMATION SizeInfo;
+ ULONG Avail;
+ PVOID IoBuffer = NULL;
+ LARGE_INTEGER Time;
+ ULONG PosixTime;
+
+ SCREQUESTMSG Request;
+
+ args = &m->u.Write;
+
+ args->Command = IO_COMMAND_DO_CONSIO;
+
+ //
+ // We need to tell the dll whether to do non-blocking io
+ // or not.
+ //
+
+ if (Fd->SystemOpenFileDesc->Flags & PSX_FD_NOBLOCK) {
+ args->Scratch1 = O_NONBLOCK;
+ } else {
+ args->Scratch1 = 0;
+ }
+
+ //
+ // Replace the given file descriptor with the one that should
+ // really be used to do the io to posix.exe. They might be
+ // different if the one passed in was created by duping 0, 1,
+ // or 2.
+ //
+
+ args->FileDes = (int)Fd->SystemOpenFileDesc->NtIoHandle;
+
+ //
+ // Update st_mtime and st_ctime.
+ //
+
+ NtQuerySystemTime(&Time);
+ if (!RtlTimeToSecondsSince1970(&Time, &PosixTime)) {
+ PosixTime = 0;
+ }
+
+ RtlEnterCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
+
+ Fd->SystemOpenFileDesc->IoNode->ModifyDataTime = PosixTime;
+ Fd->SystemOpenFileDesc->IoNode->ModifyIoNodeTime = PosixTime;
+
+ RtlLeaveCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
+
+ return TRUE;
+}
+
+
+
+BOOLEAN
+ConRead (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements read when the device being read
+ is a file.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the request.
+
+ Fd - supplies the address of the file descriptor being read.
+
+Return Value:
+
+--*/
+
+{
+ PPSX_READ_MSG args;
+ NTSTATUS st;
+ IO_STATUS_BLOCK Iosb;
+ LARGE_INTEGER ByteOffset;
+ ULONG IoBufferSize;
+ PVOID IoBuffer = NULL;
+ LARGE_INTEGER Time;
+ ULONG PosixTime;
+ PSIGDB SigDb;
+
+ SCREQUESTMSG Request;
+
+ args = &m->u.Read;
+
+ //
+ // 1003.1-90 (6.4.1.4): EIO ... The implementation supports job
+ // control, the process is in a background process group and is
+ // attempting to read from its controlling terminal, and either
+ // the process is ignoring or blocking the SIGTTIN signal or
+ // the process group of the process is orphaned.
+ //
+
+ SigDb = &p->SignalDataBase;
+
+ if (NULL != p->PsxSession->Terminal &&
+ p->PsxSession->Terminal->ForegroundProcessGroup !=
+ p->ProcessGroupId &&
+ p->PsxSession->Terminal == Fd->SystemOpenFileDesc->Terminal &&
+ ((SigDb->SignalDisposition[SIGTTIN-1].sa_handler == SIG_IGN ||
+ SIGISMEMBER(&SigDb->BlockedSignalMask, SIGTTIN)) ||
+ IsGroupOrphaned(p->ProcessGroupId))) {
+
+ m->Error = EIO;
+ return TRUE;
+ }
+ args->Command = IO_COMMAND_DO_CONSIO;
+
+ //
+ // We need to tell the dll whether to do non-blocking io
+ // or not.
+ //
+
+ if (Fd->SystemOpenFileDesc->Flags & PSX_FD_NOBLOCK) {
+ args->Scratch1 = O_NONBLOCK;
+ } else {
+ args->Scratch1 = 0;
+ }
+
+ //
+ // Replace the given file descriptor with the one that should
+ // really be used to do the io to posix.exe. They might be
+ // different if the one passed in was created by duping 0, 1,
+ // or 2.
+ //
+
+ args->FileDes = (int)Fd->SystemOpenFileDesc->NtIoHandle;
+
+ NtQuerySystemTime(&Time);
+ if (!RtlTimeToSecondsSince1970(&Time, &PosixTime)) {
+ PosixTime = 0;
+ }
+
+ //
+ // Update st_atime.
+ //
+
+ RtlEnterCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
+ Fd->SystemOpenFileDesc->IoNode->AccessDataTime = PosixTime;
+ RtlLeaveCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
+
+ return TRUE;
+}
+
+
+BOOLEAN
+ConDup (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd,
+ IN PFILEDESCRIPTOR FdDup
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements dup and dup2
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the request.
+
+ Fd - supplies the address of the file descriptor being duplicated.
+
+ FdDup - supplies the address of the duplicate file descriptor.
+
+Return Value:
+
+ ???
+--*/
+
+{
+ PPSX_DUP_MSG args;
+
+ args = &m->u.Dup;
+
+ //
+ // Copy contents of source file descriptor slot into new descriptor
+ // Note that FD_CLOEXEC must be CLEAR on FdDup.
+ //
+
+ *FdDup = *Fd;
+ FdDup->Flags &= ~PSX_FD_CLOSE_ON_EXEC;
+
+ //
+ // Increment reference count associated with the SystemOpenFile
+ // descriptor for this file.
+ //
+
+ RtlEnterCriticalSection(&SystemOpenFileLock);
+
+ Fd->SystemOpenFileDesc->HandleCount++;
+
+ RtlLeaveCriticalSection(&SystemOpenFileLock);
+
+ return TRUE;
+}
+
+
+BOOLEAN
+ConLseek (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements lseek when the device being seeked on
+ is a file.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the request.
+
+ Fd - supplies the address of the file descriptor being seekd
+
+Return Value:
+
+ ???
+
+--*/
+
+{
+ //
+ // Can't seek on a console.
+ //
+
+ m->Error = ESPIPE;
+ return TRUE;
+}
+
+
+BOOLEAN
+ConStat (
+ IN PIONODE IoNode,
+ IN HANDLE FileHandle,
+ OUT struct stat *StatBuf,
+ OUT NTSTATUS *pStatus
+ )
+/*++
+
+Routine Description:
+
+ This procedure implements stat when the device being read
+ is a file.
+
+Arguments:
+
+ IoNode - supplies a pointer to the ionode of the file for which stat is
+ requested. NULL if no active Ionode entry.
+
+ FileHandle - supplies the Nt file handle of the file .
+
+ StatBuf - Supplies the address of the statbuf portion of the message
+ associated with the request.
+
+Return Value:
+
+ TRUE
+
+--*/
+{
+ ULONG PosixTime;
+
+ StatBuf->st_mode = IoNode->Mode;
+ StatBuf->st_ino = IoNode->FileSerialNumber;
+ StatBuf->st_dev = IoNode->DeviceSerialNumber;
+ StatBuf->st_uid = IoNode->OwnerId;
+ StatBuf->st_gid = IoNode->GroupId;
+ StatBuf->st_size = 0;
+
+ StatBuf->st_atime = IoNode->AccessDataTime;
+ StatBuf->st_mtime = IoNode->ModifyDataTime;
+ StatBuf->st_ctime = IoNode->ModifyIoNodeTime;
+
+ StatBuf->st_nlink = 1;
+
+ return TRUE;
+}
diff --git a/private/posix/psxss/consignl.c b/private/posix/psxss/consignl.c
new file mode 100644
index 000000000..22ab3f6f2
--- /dev/null
+++ b/private/posix/psxss/consignl.c
@@ -0,0 +1,86 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ consignl.c
+
+Abstract:
+
+ This module contains the handler for signals received from PSXSES.
+
+Author:
+
+ Avi Nathan (avin) 17-Jul-1991
+
+Revision History:
+
+ Ellen Aycock-Wright (ellena) 15-Sept-1991 Modified for POSIX
+
+--*/
+
+#include "psxsrv.h"
+
+#define NTPSX_ONLY
+#include "sesport.h"
+
+#include <windows.h>
+#include <wincon.h>
+
+NTSTATUS
+PsxCtrlSignalHandler(
+ IN OUT PVOID RequestMsg
+ )
+{
+ PPSX_PROCESS Process;
+ PPSX_SESSION Session;
+ PPSXSESREQUESTMSG Msg;
+ PPSX_PROCESS p;
+ int Signal;
+
+ Msg = RequestMsg;
+
+ switch (Msg->d.Signal.Type) {
+ case PSX_SIGINT:
+ Signal = SIGINT;
+ break;
+ case PSX_SIGQUIT:
+ Signal = SIGQUIT;
+ break;
+ case PSX_SIGTSTP:
+ Signal = SIGTSTP;
+ break;
+ case PSX_SIGKILL:
+ Signal = SIGKILL;
+ break;
+ default:
+ KdPrint(("PSXSS: Unknown signal type.\n"));
+ Signal = 0;
+ break;
+ }
+
+ Session = PsxLocateSessionByUniqueId(Msg->UniqueId);
+ if (NULL == Session) {
+ KdPrint(("PSXSS: ConSignl: could not locate session\n"));
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Send the signal to every process associated with the session.
+ //
+
+ AcquireProcessStructureLock();
+
+ for (p = FirstProcess; p < LastProcess; p++) {
+ if (p->Flags & P_FREE)
+ continue;
+ if (p->PsxSession == Session) {
+ PsxSignalProcess(p, Signal);
+ }
+ }
+
+ ReleaseProcessStructureLock();
+
+ return STATUS_SUCCESS;
+}
diff --git a/private/posix/psxss/conthrds.c b/private/posix/psxss/conthrds.c
new file mode 100644
index 000000000..8747eebbf
--- /dev/null
+++ b/private/posix/psxss/conthrds.c
@@ -0,0 +1,201 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ conthrds.c
+
+Abstract:
+
+ This module contains the Server Listen&Request threads
+ procedures for the Console Session.
+
+Author:
+
+ Avi Nathan (avin) 17-Jul-1991
+
+Revision History:
+
+ Ellen Aycock-Wright (ellena) 15-Sept-1991 Modified for POSIX
+
+--*/
+
+#include <stdio.h>
+#include "psxsrv.h"
+
+#define NTPSX_ONLY
+#include "sesport.h"
+
+
+NTSTATUS
+PsxSessionHandleConnectionRequest(
+ IN PPSXSESREQUESTMSG Message
+ )
+{
+ NTSTATUS Status;
+ PPSXSESCONNECTINFO ConnectionInfoIn = &Message->ConnectionRequest;
+ SCCONNECTINFO ConnectionInfoOut;
+ ULONG ConnectionInfoOutLength;
+ STRING SessionPortName;
+ UNICODE_STRING SessionPortName_U;
+ SECURITY_QUALITY_OF_SERVICE DynamicQos;
+ CHAR SessionName[PSX_SES_BASE_PORT_NAME_LENGTH];
+ HANDLE SessionPort;
+ HANDLE PsxSessionCommPort;
+ int Id;
+
+ ConnectionInfoOutLength = sizeof(ConnectionInfoOut);
+
+ Id = ConnectionInfoIn->In.SessionUniqueId;
+
+ PSX_GET_SESSION_PORT_NAME(SessionName, Id);
+
+ RtlInitAnsiString(&SessionPortName, SessionName);
+ Status = RtlAnsiStringToUnicodeString(&SessionPortName_U,
+ &SessionPortName, TRUE);
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ DynamicQos.ImpersonationLevel = SecurityImpersonation;
+ DynamicQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ DynamicQos.EffectiveOnly = TRUE;
+
+ //
+ // Get the session communication port handle. This handle will
+ // be used to send session requests to psxses.exe for this
+ // session.
+ //
+
+ Status = NtConnectPort(&SessionPort, &SessionPortName_U,
+ &DynamicQos, NULL, NULL, NULL,
+ (PVOID)&ConnectionInfoOut, &ConnectionInfoOutLength);
+
+ RtlFreeUnicodeString(&SessionPortName_U);
+
+ if (!NT_SUCCESS(Status)) {
+
+ NTSTATUS st2;
+
+ // Reject
+ st2 = NtAcceptConnectPort(&PsxSessionCommPort, NULL,
+ (PPORT_MESSAGE)Message, FALSE, NULL, NULL);
+
+ if (NT_SUCCESS(st2)) {
+ NtClose(PsxSessionCommPort);
+ }
+
+ return Status;
+ }
+
+ // Accept the connection
+
+ ConnectionInfoIn->Out.SessionPortHandle = SessionPort;
+
+ //
+ // PsxSessionCommPort is not used for Reply. Instead,
+ // the port is created with ReceiveAny==TRUE and we wait
+ // on the connection port.
+ //
+
+ Status = NtAcceptConnectPort(&PsxSessionCommPort, NULL,
+ (PPORT_MESSAGE)Message, TRUE, NULL, NULL);
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Add an entry to the ConnectingSessions list, saying
+ // that the session exists but has not yet asked to have
+ // a process created to be the session leader.
+ //
+
+ Status = AddConnectingTerminal(Id, PsxSessionCommPort, SessionPort);
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ Status = NtCompleteConnectPort(PsxSessionCommPort);
+ ASSERT(NT_SUCCESS(Status));
+
+ return Status;
+}
+
+// for debug
+int DbgBadRQ;
+
+NTSTATUS
+PsxSessionRequestThread(
+ PVOID Parameter
+ )
+{
+ NTSTATUS Status;
+ PSXSESREQUESTMSG ReceiveMsg, *pReplyMsg;
+ int MessageType;
+
+ pReplyMsg = NULL;
+
+ for (;;) {
+ Status = NtReplyWaitReceivePort(PsxSessionPort, NULL,
+ (PPORT_MESSAGE)pReplyMsg, (PPORT_MESSAGE)&ReceiveMsg);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: NtReplyWaitReceivePort: 0x%x\n",
+ Status));
+ if (STATUS_INVALID_HANDLE == Status ||
+ STATUS_OBJECT_TYPE_MISMATCH == Status) {
+ DbgBadRQ = ReceiveMsg.Request;
+ KdPrint(("PSXSS: SessionRequestThread exits, "
+ "rq was %d.\n", ReceiveMsg.Request));
+ break;
+ }
+ pReplyMsg = NULL;
+ continue;
+ }
+
+ MessageType = ReceiveMsg.h.u2.s2.Type;
+
+ if (MessageType == LPC_CONNECTION_REQUEST) {
+ PsxSessionHandleConnectionRequest( &ReceiveMsg );
+ pReplyMsg = NULL;
+ continue;
+ }
+ if (MessageType == LPC_CLIENT_DIED ||
+ MessageType == LPC_PORT_CLOSED
+ ) {
+ pReplyMsg = NULL;
+ continue;
+ }
+
+ if (MessageType == LPC_DEBUG_EVENT ||
+ MessageType == LPC_ERROR_EVENT ||
+ MessageType == LPC_EXCEPTION
+ ) {
+ pReplyMsg = NULL;
+ continue;
+ }
+
+ if (MessageType != LPC_REQUEST) {
+ KdPrint(("PSXSS: SesRqThread got type %d\n",
+ MessageType));
+ pReplyMsg = NULL;
+ continue;
+ } else {
+ switch (ReceiveMsg.Request) {
+ case SesConCreate:
+ Status = PsxCreateConSession(&ReceiveMsg);
+ break;
+ case SesConSignal:
+ Status = PsxCtrlSignalHandler(&ReceiveMsg);
+ break;
+ default:
+ Status = 0;
+ KdPrint(("PSXSS: Unknown Session request: %d\n",
+ ReceiveMsg.Request));
+ }
+
+ pReplyMsg = &ReceiveMsg;
+ pReplyMsg->Status = Status;
+ }
+ }
+
+ return Status;
+}
diff --git a/private/posix/psxss/fdio.c b/private/posix/psxss/fdio.c
new file mode 100644
index 000000000..0d4198084
--- /dev/null
+++ b/private/posix/psxss/fdio.c
@@ -0,0 +1,810 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ fdio.c
+
+Abstract:
+
+ Implementation of PSX file descriptor io.
+
+Author:
+
+ Mark Lucovsky (markl) 08-Mar-1989
+
+Revision History:
+
+--*/
+
+#include "psxsrv.h"
+
+//
+// This lock must be held while updating handle counts on system open file
+// descriptors.
+//
+
+RTL_CRITICAL_SECTION SystemOpenFileLock;
+
+//
+// This lock must be held while updating reference counts on IONODES, and
+// when scanning the IoNodeHashTable searching for an IoNode.
+//
+
+RTL_CRITICAL_SECTION IoNodeHashTableLock;
+
+
+//
+// IoNode Id Hash Table.
+//
+// Given a FileSerialNumber and DeviceSerialNumber, an IONODE can be located in
+// the IoNodeHashTable.
+//
+
+LIST_ENTRY IoNodeHashTable[IONODEHASHSIZE];
+
+
+BOOLEAN
+ReferenceOrCreateIoNode (
+ IN dev_t DeviceSerialNumber,
+ IN ino_t FileSerialNumber,
+ IN BOOLEAN FindOnly,
+ OUT PIONODE *IoNode
+ )
+
+/*++
+
+Routine Description:
+
+ This routine references an existing IoNode, or creates a new IoNode
+ if one can not be found. It returns with the IoNode's reference count
+ adjusted, and the IoNodes lock held.
+
+Arguments:
+
+ DeviceSerialNumber - Supplies the device serial number of the IoNode.
+
+ FileSerialNumber - Supplies the file serial number of the IoNode.
+
+ FindOnly - If set, just return pointer to ionode; do not update ref count.
+
+ IoNode - Returns the address of either the new or existing IoNode associated
+ with the specified serial numbers.
+
+Return Value:
+
+ TRUE - An existing IoNode was located.
+
+ FALSE - A new IoNode was created.
+
+--*/
+
+{
+ PIONODE ionode;
+ PLIST_ENTRY head, next;
+ NTSTATUS st;
+
+ head = &IoNodeHashTable[
+ SERIALNUMBERTOHASHINDEX(DeviceSerialNumber,FileSerialNumber)];
+
+ //
+ // Lock IoNodeHashTable
+ //
+
+ RtlEnterCriticalSection(&IoNodeHashTableLock);
+
+ next = head->Flink;
+ while (next != head) {
+ ionode = CONTAINING_RECORD(next,IONODE,IoNodeHashLinks);
+ if ( (ionode->DeviceSerialNumber == DeviceSerialNumber) &&
+ (ionode->FileSerialNumber == FileSerialNumber) ) {
+
+ RtlEnterCriticalSection(&ionode->IoNodeLock);
+
+ if (!FindOnly) {
+ // Increment the IoNode reference count
+ ionode->ReferenceCount++;
+ }
+
+ RtlLeaveCriticalSection(&IoNodeHashTableLock);
+ *IoNode = ionode;
+ return TRUE;
+ }
+ next = next->Flink;
+ }
+
+ if (FindOnly) {
+ RtlLeaveCriticalSection(&IoNodeHashTableLock);
+ return FALSE;
+ }
+
+ //
+ // Allocate a new IoNode
+ //
+
+ ionode = RtlAllocateHeap(PsxHeap, 0,sizeof(IONODE));
+
+ //
+ // Initialize the IoNode reference count
+ // Initialize the IoNodeLock and insert the IoNode into the IoNodeHashTable
+ // Initialize the device and file serial number fields
+ // Initialize the list of flocks
+ //
+
+ ionode->ReferenceCount = 1;
+
+ st = RtlInitializeCriticalSection(&ionode->IoNodeLock);
+ ASSERT(NT_SUCCESS(st));
+
+ InsertTailList(head, &ionode->IoNodeHashLinks);
+
+ ionode->DeviceSerialNumber = DeviceSerialNumber;
+ ionode->FileSerialNumber = FileSerialNumber;
+
+ InitializeListHead(&ionode->Flocks);
+ InitializeListHead(&ionode->Waiters);
+
+ //
+ // Lock the IoNode and release the IoNodeHashTableLock
+ //
+
+ RtlEnterCriticalSection(&ionode->IoNodeLock);
+ RtlLeaveCriticalSection(&IoNodeHashTableLock);
+
+ InitializeListHead(&ionode->Flocks);
+
+ ionode->Junked = FALSE;
+
+ *IoNode = ionode;
+
+ return FALSE;
+}
+
+
+VOID
+DereferenceIoNode (
+ IN PIONODE IoNode
+ )
+
+/*++
+
+Routine Description:
+
+ This routine dereferences and possibly deallocates the specified IoNode.
+
+Arguments:
+
+ IoNode - Supplies the address of the IoNode to be dereferenced.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RtlEnterCriticalSection(&IoNodeHashTableLock);
+
+ if (0 == --IoNode->ReferenceCount) {
+ RemoveEntryList(&IoNode->IoNodeHashLinks);
+
+ // Call close routine.
+
+ RtlDeleteCriticalSection(&IoNode->IoNodeLock);
+
+ if (IoNode->IoVectors->IoNodeCloseRoutine) {
+ (IoNode->IoVectors->IoNodeCloseRoutine)(IoNode);
+ }
+
+ //
+ // All flocks should have been freed by now.
+ //
+
+ ASSERT(IsListEmpty(&IoNode->Flocks));
+
+ RtlFreeHeap(PsxHeap, 0,IoNode);
+ }
+ RtlLeaveCriticalSection(&IoNodeHashTableLock);
+}
+
+
+PFILEDESCRIPTOR
+AllocateFd(
+ IN PPSX_PROCESS p,
+ IN ULONG Start,
+ OUT PULONG Index
+ )
+
+/*++
+
+Routine Description:
+
+ This function scans the specified process' open file table searching
+ for the lowest free slot. Once a free slot is located, its address
+ is returned. If no free slot is found, NULL is returned.
+
+Arguments:
+
+ p - Supplies a pointer to the process whose open file table is to be
+ scanned.
+
+ Start - The file table is scanned starting from descriptor 'Start':
+ Zero for the beginning of the table, and so forth.
+
+ Index - If a file descriptor is located, this parameter returns the
+ index of the allocated file descriptor.
+
+Return Value:
+
+ NULL - No free file descriptor was located.
+
+ NON-NULL - The address of the lowest free file descriptor greater than
+ or equal to 'Start' is returned.
+
+--*/
+
+{
+ ULONG i;
+ PFILEDESCRIPTOR fd;
+
+ fd = &p->ProcessFileTable[Start];
+
+
+ for (i = Start; i < OPEN_MAX; i++, fd++) {
+
+ //
+ // XXX.mjb: CLIENT_OPEN: would also have to make sure not to
+ // allocate an FD here that was obtained via clientopen.
+ //
+
+ if (NULL == fd->SystemOpenFileDesc) {
+ *Index = i;
+ fd->Flags = 0;
+ return fd;
+ }
+ }
+ return NULL;
+}
+
+
+BOOLEAN
+DeallocateFd(
+ IN PPSX_PROCESS p,
+ IN ULONG Index
+ )
+
+/*++
+
+Routine Description:
+
+ This function deallocates the file descriptor from the specified
+ process' open file table. If the file is not allocated, then an
+ error is returned.
+
+ If the file descriptor was allocated, then the system open file that
+ it refers to is dereferenced. This could cause the system open
+ file, and possibly the associated IoNode, to be deallocated.
+
+Arguments:
+
+ p - Supplies a pointer to the process whose open file table is being
+ scanned.
+
+ Index - Supplies the index of the file descriptor to be deallocated.
+
+Return Value:
+
+ TRUE - The file descriptor was successfully deallocated.
+
+ FALSE - The file descriptor did not refer to an allocated file descriptor.
+
+--*/
+
+{
+
+ PFILEDESCRIPTOR Fd;
+ PSYSTEMOPENFILE SystemOpenFile;
+
+
+ Fd = &p->ProcessFileTable[Index];
+
+ SystemOpenFile = Fd->SystemOpenFileDesc;
+ if (NULL == SystemOpenFile) {
+ return FALSE;
+ }
+
+ IoClose(p,Fd);
+
+ Fd->SystemOpenFileDesc = (PSYSTEMOPENFILE)NULL;
+
+ RtlEnterCriticalSection(&SystemOpenFileLock);
+
+ if (--SystemOpenFile->HandleCount == 0) {
+ DeallocateSystemOpenFile(p, SystemOpenFile);
+ }
+
+ RtlLeaveCriticalSection(&SystemOpenFileLock);
+
+ return TRUE;
+}
+
+
+PFILEDESCRIPTOR
+FdIndexToFd(
+ IN PPSX_PROCESS p,
+ IN ULONG Index
+ )
+
+/*++
+
+Routine Description:
+
+ This function translates a file descriptor index into
+ a pointer to a file descriptor.
+
+Arguments:
+
+ p - Supplies the process whose file descriptor table is to be used
+
+ Index - Supplies the file descriptor index to translate
+
+Return Value:
+
+ NULL - the file descriptor index is not in range, or specifies a
+ file descriptor that is not open.
+
+ NON-NULL - Returns the address of the file descriptor associated with the
+ index.
+
+--*/
+
+{
+
+ PFILEDESCRIPTOR Fd;
+
+ if ( !ISFILEDESINRANGE(Index) ) {
+
+ return NULL;
+ }
+
+ Fd = &p->ProcessFileTable[Index];
+
+ if ( !Fd->SystemOpenFileDesc ) {
+
+ return NULL;
+ }
+
+ return Fd;
+}
+
+
+
+
+PSYSTEMOPENFILE
+AllocateSystemOpenFile(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function allocates and references a system open file.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NON-NULL - Returns the address of a system open file.
+
+--*/
+
+{
+
+ PSYSTEMOPENFILE SystemOpenFile;
+
+ //
+ // Grab system open file lock
+ //
+
+ RtlEnterCriticalSection(&SystemOpenFileLock);
+
+ SystemOpenFile = RtlAllocateHeap(PsxHeap, 0, sizeof(SYSTEMOPENFILE));
+ if (NULL == SystemOpenFile) {
+ RtlLeaveCriticalSection(&SystemOpenFileLock);
+ return NULL;
+ }
+
+ SystemOpenFile->HandleCount = 1;
+ SystemOpenFile->ReadHandleCount = 0;
+ SystemOpenFile->WriteHandleCount = 0;
+ SystemOpenFile->Flags = 0;
+
+ //
+ // Release system open file lock
+ //
+
+ RtlLeaveCriticalSection(&SystemOpenFileLock);
+
+ return SystemOpenFile;
+
+}
+
+
+VOID
+DeallocateSystemOpenFile(
+ IN PPSX_PROCESS p,
+ IN PSYSTEMOPENFILE SystemOpenFile
+ )
+
+/*++
+
+Routine Description:
+
+ This function deallocates a system open file. If may cause the deallocation
+ of the open file's associated IoNode.
+
+ This function is called with the system open file lock held.
+
+Arguments:
+
+ SystemOpenFile - Supplies the address of the system open file to deallocate.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PIONODE IoNode;
+
+ IoNode = SystemOpenFile->IoNode;
+
+ IoLastClose(p, SystemOpenFile);
+
+ RtlFreeHeap(PsxHeap, 0,SystemOpenFile);
+
+ DereferenceIoNode(IoNode);
+
+}
+
+
+VOID
+ForkProcessFileTable(
+ IN PPSX_PROCESS ForkProcess,
+ IN PPSX_PROCESS NewProcess
+ )
+
+/*++
+
+Routine Description:
+
+ This function forks the open file table of the calling process. It does
+ this by copying each file descriptor in the fork process' table to a
+ descriptor in the new process' table. For each descriptor that is opened
+ (references a system open file descriptor), the reference count is
+ incremented.
+
+Arguments:
+
+ ForkProcess - Supplies a pointer to the process that is the parent in the
+ fork operation.
+
+ NewProcess - Supplies a pointer to the process that is the new process in
+ the fork operation.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ LONG i;
+ PFILEDESCRIPTOR ForkFd, NewFd;
+
+ ForkFd = ForkProcess->ProcessFileTable;
+ NewFd = NewProcess->ProcessFileTable;
+
+ //
+ // Grab system open file lock
+ //
+
+ RtlEnterCriticalSection(&SystemOpenFileLock);
+
+ for (i = 0; i < OPEN_MAX; i++, NewFd++, ForkFd++) {
+ //
+ // Copy the file descriptor, then up the reference
+ // to the associated system open file descriptor
+ //
+
+ *NewFd = *ForkFd;
+
+ if (NULL != ForkFd->SystemOpenFileDesc
+ && (PSYSTEMOPENFILE)1 != ForkFd->SystemOpenFileDesc) {
+ ForkFd->SystemOpenFileDesc->HandleCount++;
+ IoNewHandle(NewProcess, NewFd);
+ }
+ }
+
+ //
+ // Release system open file lock
+ //
+
+ RtlLeaveCriticalSection(&SystemOpenFileLock);
+}
+
+
+VOID
+ExecProcessFileTable(
+ IN PPSX_PROCESS p
+ )
+
+/*++
+
+Routine Description:
+
+ This function execs the open file table of the calling process.
+ It does this by closing each file descriptor whose close on
+ exec flag is set.
+
+Arguments:
+
+ p - Supplies the process that is doing an exec.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LONG i;
+ PFILEDESCRIPTOR Fd;
+
+ Fd = p->ProcessFileTable;
+
+ RtlEnterCriticalSection(&SystemOpenFileLock);
+
+ for (i = 0; i < OPEN_MAX; i++, Fd++) {
+ if (NULL != Fd->SystemOpenFileDesc &&
+ Fd->Flags & PSX_FD_CLOSE_ON_EXEC) {
+
+ IoClose(p,Fd);
+ if (--(Fd->SystemOpenFileDesc->HandleCount) == 0) {
+ DeallocateSystemOpenFile(p,
+ Fd->SystemOpenFileDesc);
+ }
+ }
+ }
+
+ RtlLeaveCriticalSection(&SystemOpenFileLock);
+}
+
+VOID
+CloseProcessFileTable(
+ IN PPSX_PROCESS p
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called during process termination to close
+ all open filehandles held by the process.
+
+Arguments:
+
+ p - Supplies the address of the process whose open file table is
+ being closed.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LONG i;
+ PFILEDESCRIPTOR Fd;
+
+ Fd = p->ProcessFileTable;
+
+ // Grab system open file lock
+
+ RtlEnterCriticalSection(&SystemOpenFileLock);
+
+ for (i = 0; i < OPEN_MAX; i++, Fd++) {
+ if (NULL != Fd->SystemOpenFileDesc) {
+ (void)DeallocateFd(p, i);
+ }
+ }
+
+ // Release system open file lock
+
+ RtlLeaveCriticalSection(&SystemOpenFileLock);
+}
+
+BOOLEAN
+IoOpenNewHandle (
+ IN PPSX_PROCESS p,
+ IN PFILEDESCRIPTOR Fd,
+ IN PPSX_API_MSG m
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called after a new handle has been created and
+ initialized. Its function is to adjust the read/write handle counts
+ and then call the type specific open new handle routine.
+
+ This function is only called from open.
+
+Arguments:
+
+ p - Supplies the process creating a new handle
+
+ Fd - Supplies the address of the initialized file descriptor
+
+ m - Supplies the open message
+
+Return Value:
+
+ TRUE - A reply to the open message should be generated.
+
+ FALSE - No reply should be generated.
+
+--*/
+
+{
+ RtlEnterCriticalSection(&SystemOpenFileLock);
+
+ if (Fd->SystemOpenFileDesc->Flags & PSX_FD_READ) {
+ Fd->SystemOpenFileDesc->ReadHandleCount++;
+ }
+
+ if (Fd->SystemOpenFileDesc->Flags & PSX_FD_WRITE) {
+ Fd->SystemOpenFileDesc->WriteHandleCount++;
+ }
+
+ RtlLeaveCriticalSection(&SystemOpenFileLock);
+
+ if (Fd->SystemOpenFileDesc->IoNode->IoVectors->OpenNewHandleRoutine) {
+ return (Fd->SystemOpenFileDesc->IoNode->IoVectors->OpenNewHandleRoutine)(p,Fd,m);
+ }
+ return TRUE;
+}
+
+VOID
+IoNewHandle (
+ IN PPSX_PROCESS p,
+ IN PFILEDESCRIPTOR Fd
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called after a new handle has been created and
+ initialized. Its function is to adjust the read/write handle counts
+ and then call the type specific new handle routine.
+
+ This function is not called in response to an open. Only handles
+ created through pipe, dup, or fork get called in this way. Open
+ is different because it might need to block so it
+ can implement an open protocol (named pipe opens...);
+
+Arguments:
+
+ p - Supplies the process creating a new handle
+
+ Fd - Supplies the address of the initialized file descriptor
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RtlEnterCriticalSection(&SystemOpenFileLock);
+
+ if (Fd->SystemOpenFileDesc->Flags & PSX_FD_READ) {
+ Fd->SystemOpenFileDesc->ReadHandleCount++;
+ }
+
+ if (Fd->SystemOpenFileDesc->Flags & PSX_FD_WRITE) {
+ Fd->SystemOpenFileDesc->WriteHandleCount++;
+ }
+
+ RtlLeaveCriticalSection(&SystemOpenFileLock);
+
+ if (Fd->SystemOpenFileDesc->IoNode->IoVectors->NewHandleRoutine) {
+ (Fd->SystemOpenFileDesc->IoNode->IoVectors->NewHandleRoutine)(p,Fd);
+ }
+}
+
+
+VOID
+IoClose(
+ IN PPSX_PROCESS p,
+ IN PFILEDESCRIPTOR Fd
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called whenever a handle is deleted.
+ Its function is to adjust the read/write handle counts
+ and then call the type specific close routine.
+
+Arguments:
+
+ p - Supplies the process closing a handle
+
+ Fd - Supplies the address of the initialized file descriptor
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RtlEnterCriticalSection(&SystemOpenFileLock);
+
+ if (Fd->SystemOpenFileDesc->Flags & PSX_FD_READ) {
+ Fd->SystemOpenFileDesc->ReadHandleCount--;
+ }
+
+ if (Fd->SystemOpenFileDesc->Flags & PSX_FD_WRITE) {
+ Fd->SystemOpenFileDesc->WriteHandleCount--;
+ }
+
+ RtlLeaveCriticalSection(&SystemOpenFileLock);
+ ReleaseFlocksByPid(Fd->SystemOpenFileDesc->IoNode, p->Pid);
+
+ if (Fd->SystemOpenFileDesc->IoNode->IoVectors->CloseRoutine) {
+ (Fd->SystemOpenFileDesc->IoNode->IoVectors->CloseRoutine)(p,Fd);
+ }
+}
+
+VOID
+IoLastClose (
+ IN PPSX_PROCESS p,
+ IN PSYSTEMOPENFILE SystemOpenFile
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called whenever the last handle is deleted.
+ Its function is to call the type specific close routine.
+
+Arguments:
+
+ p - Supplies the process closing a handle
+
+ Fd - Supplies the address of the initialized file descriptor
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ if (SystemOpenFile->IoNode->IoVectors->LastCloseRoutine) {
+ (SystemOpenFile->IoNode->IoVectors->LastCloseRoutine)
+ (p, SystemOpenFile);
+ }
+}
diff --git a/private/posix/psxss/fileio.c b/private/posix/psxss/fileio.c
new file mode 100644
index 000000000..92424065c
--- /dev/null
+++ b/private/posix/psxss/fileio.c
@@ -0,0 +1,820 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ fileio.c
+
+Abstract:
+
+ This module implements server performed file io
+
+Author:
+
+ Mark Lucovsky (markl) 27-Nov-1989
+
+Revision History:
+
+--*/
+
+
+#include <sys/stat.h>
+#include <time.h>
+#include <wchar.h>
+#include "psxsrv.h"
+
+BOOLEAN
+FileRead (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd
+ );
+
+BOOLEAN
+FileWrite (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd
+ );
+
+BOOLEAN
+FileDup (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd,
+ IN PFILEDESCRIPTOR FdDup
+ );
+
+BOOLEAN
+FileLseek (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd
+ );
+
+BOOLEAN
+FileStat (
+ IN PIONODE IoNode,
+ IN HANDLE FileHandle,
+ OUT struct stat *StatBuf,
+ OUT NTSTATUS *pStatus
+ );
+
+void
+FindOwnerModeFile(
+ IN HANDLE FileHandle,
+ OUT struct stat *StatBuf
+ );
+
+VOID
+FileLastClose (
+ IN PPSX_PROCESS p,
+ IN PSYSTEMOPENFILE SystemOpenFile
+ )
+{
+ NTSTATUS st;
+ IO_STATUS_BLOCK Iosb;
+ FILE_DISPOSITION_INFORMATION Disp;
+ wchar_t buf[PATH_MAX];
+ UNICODE_STRING U;
+ ANSI_STRING A;
+ OBJECT_ATTRIBUTES Obj;
+ HANDLE hDir;
+
+ if (!SystemOpenFile->IoNode->Junked) {
+ st = NtClose(SystemOpenFile->NtIoHandle);
+ ASSERT(NT_SUCCESS(st));
+ return;
+ }
+
+ //
+ // This file has been moved to the junkyard, and should now
+ // be deleted.
+ //
+
+ Disp.DeleteFile = TRUE;
+ st = NtSetInformationFile(SystemOpenFile->NtIoHandle,
+ &Iosb, &Disp, sizeof(Disp), FileDispositionInformation);
+ if (!NT_SUCCESS(st)) {
+ KdPrint(("PSXSS: FileLastClose: SetInfo: 0x%x\n", st));
+ }
+ st = NtClose(SystemOpenFile->NtIoHandle);
+ ASSERT(NT_SUCCESS(st));
+
+ //
+ // Try to delete the directory that held the junked file.
+ //
+
+ swprintf(buf, L"\\DosDevices\\%wc:\\%ws",
+ SystemOpenFile->IoNode->DeviceSerialNumber, PSX_JUNK_DIR);
+
+ U.Buffer = buf;
+ U.Length = wcslen(buf) * sizeof(wchar_t);
+ U.MaximumLength = sizeof(buf);
+
+ InitializeObjectAttributes(&Obj, &U, 0, NULL, NULL);
+
+ st = NtOpenFile(&hDir, SYNCHRONIZE | DELETE,
+ &Obj, &Iosb, SHARE_ALL,
+ FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
+ if (!NT_SUCCESS(st)) {
+ //null
+ } else {
+ Disp.DeleteFile = TRUE;
+
+ st = NtSetInformationFile(hDir, &Iosb,
+ &Disp, sizeof(Disp),
+ FileDispositionInformation);
+ NtClose(hDir);
+ }
+}
+
+
+PSXIO_VECTORS FileVectors = {
+ NULL,
+ NULL,
+ NULL,
+ FileLastClose,
+ NULL,
+ FileRead,
+ FileWrite,
+ FileDup,
+ FileLseek,
+ FileStat
+ };
+
+
+BOOLEAN
+FileWrite (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements write when the device being written
+ is a file.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the request.
+
+ Fd - supplies the address of the file descriptor being written.
+
+Return Value:
+
+--*/
+
+{
+ PPSX_WRITE_MSG args;
+ NTSTATUS st;
+ IO_STATUS_BLOCK Iosb;
+ LARGE_INTEGER ByteOffset;
+ ULONG IoBufferSize;
+ FILE_FS_SIZE_INFORMATION SizeInfo;
+ ULONG Avail;
+ LARGE_INTEGER Time;
+ ULONG PosixTime;
+
+ PVOID IoBuffer = NULL;
+
+ args = &m->u.Write;
+
+ // Allocate buffer in server
+
+ IoBufferSize = args->Nbytes;
+
+ st = NtAllocateVirtualMemory(NtCurrentProcess(), &IoBuffer, 0,
+ &IoBufferSize, MEM_COMMIT, PAGE_READWRITE);
+ if (!NT_SUCCESS(st)) {
+ m->Error = ENOMEM;
+ return TRUE;
+ }
+
+ // Read data from user buffer to server buffer
+
+ st = NtReadVirtualMemory(p->Process, args->Buf, IoBuffer, args->Nbytes,
+ NULL);
+ if (!NT_SUCCESS(st)) {
+ m->Error = PsxStatusToErrno(st);
+ goto out;
+ }
+
+ if (Fd->SystemOpenFileDesc->Flags & PSX_FD_APPEND) {
+ ByteOffset = RtlConvertLongToLargeInteger(
+ FILE_WRITE_TO_END_OF_FILE);
+ } else {
+ ByteOffset = RtlConvertLongToLargeInteger(
+ FILE_USE_FILE_POINTER_POSITION);
+ }
+
+ st = NtWriteFile(Fd->SystemOpenFileDesc->NtIoHandle, NULL,
+ NULL, NULL, &Iosb, IoBuffer, args->Nbytes, &ByteOffset, NULL);
+
+ if (NT_SUCCESS(st)) {
+
+ NtQuerySystemTime(&Time);
+ if (!RtlTimeToSecondsSince1970(&Time, &PosixTime)) {
+ PosixTime = 0;
+ }
+
+ RtlEnterCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
+ Fd->SystemOpenFileDesc->IoNode->ModifyDataTime = PosixTime;
+ Fd->SystemOpenFileDesc->IoNode->ModifyIoNodeTime = PosixTime;
+ RtlLeaveCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
+ m->ReturnValue = Iosb.Information;
+ goto out;
+ }
+
+ switch (st) {
+ case STATUS_DISK_FULL:
+ while (0 != --args->Nbytes) {
+ st = NtWriteFile(Fd->SystemOpenFileDesc->NtIoHandle,
+ NULL, NULL, NULL, &Iosb, IoBuffer,
+ args->Nbytes, &ByteOffset, NULL);
+ if (NT_SUCCESS(st)) {
+ m->ReturnValue = Iosb.Information;
+ goto out;
+ }
+ }
+ m->Error = ENOSPC;
+ break;
+ default:
+ m->Error = EIO;
+ break;
+ }
+
+out:
+ st = NtFreeVirtualMemory(NtCurrentProcess(), &IoBuffer, &IoBufferSize,
+ MEM_RELEASE);
+ if (!NT_SUCCESS(st) ) {
+ m->Error = ENOMEM;
+ }
+ return TRUE;
+}
+
+
+
+
+BOOLEAN
+FileRead (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements read when the device being read
+ is a file.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the request.
+
+ Fd - supplies the address of the file descriptor being read.
+
+Return Value:
+
+--*/
+
+{
+ PPSX_READ_MSG args;
+ PPSX_READDIR_MSG args2;
+ NTSTATUS st;
+ IO_STATUS_BLOCK Iosb;
+ LARGE_INTEGER ByteOffset;
+ ULONG IoBufferSize;
+ LARGE_INTEGER Time;
+ ULONG PosixTime;
+
+ UCHAR Buf[sizeof(FILE_NAMES_INFORMATION) +
+ NAME_MAX * sizeof(WCHAR)];
+ PFILE_NAMES_INFORMATION pNamesInfo = (PVOID)Buf;
+
+ PVOID IoBuffer = NULL;
+
+ args2 = &m->u.ReadDir;
+ args = &m->u.Read;
+
+ //
+ // Update the access time on the ionode.
+ //
+
+ NtQuerySystemTime(&Time);
+ if (!RtlTimeToSecondsSince1970(&Time, &PosixTime)) {
+ PosixTime = 0;
+ }
+
+ RtlEnterCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
+ Fd->SystemOpenFileDesc->IoNode->AccessDataTime = PosixTime;
+ RtlLeaveCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
+
+
+ if (S_ISDIR(Fd->SystemOpenFileDesc->IoNode->Mode)) {
+ UNICODE_STRING U;
+ ANSI_STRING A;
+
+ st = NtQueryDirectoryFile(
+ Fd->SystemOpenFileDesc->NtIoHandle,
+ NULL, NULL, NULL, &Iosb,
+ &Buf, sizeof(Buf),
+ FileNamesInformation, TRUE, NULL,
+ args2->RestartScan
+ );
+ if (STATUS_BUFFER_OVERFLOW == st) {
+ m->Error = ENAMETOOLONG;
+ return TRUE;
+ }
+ if (STATUS_NO_MORE_FILES == st) {
+ m->ReturnValue = 0;
+ return TRUE;
+ }
+ if (!NT_SUCCESS(st)) {
+ m->Error = PsxStatusToErrno(st);
+ return TRUE;
+ }
+ U.Length = U.MaximumLength = (USHORT)pNamesInfo->FileNameLength;
+ U.Buffer = pNamesInfo->FileName;
+
+ st = RtlUnicodeStringToAnsiString(&A, &U, TRUE);
+ if (!NT_SUCCESS(st)) {
+ m->Error = ENOMEM;
+ return TRUE;
+ }
+
+ m->ReturnValue = A.Length;
+ st = NtWriteVirtualMemory(p->Process, args2->Buf,
+ A.Buffer, A.Length, NULL);
+
+ RtlFreeAnsiString(&A);
+ if (!NT_SUCCESS(st)) {
+ m->Error = EIO;
+ return TRUE;
+ }
+ return TRUE;
+ }
+
+ IoBufferSize = args->Nbytes;
+
+ st = NtAllocateVirtualMemory(NtCurrentProcess(), &IoBuffer, 0,
+ &IoBufferSize, MEM_COMMIT, PAGE_READWRITE);
+ if (!NT_SUCCESS(st)) {
+ m->Error = ENOMEM;
+ return TRUE;
+ }
+
+ ByteOffset = RtlConvertLongToLargeInteger(
+ FILE_USE_FILE_POINTER_POSITION);
+
+ st = NtReadFile(Fd->SystemOpenFileDesc->NtIoHandle, NULL, NULL, NULL,
+ &Iosb, IoBuffer, args->Nbytes, &ByteOffset, NULL);
+
+ if (STATUS_END_OF_FILE == st) {
+ m->ReturnValue = 0;
+ goto out;
+ }
+ if (!NT_SUCCESS(st)) {
+ m->Error = EIO;
+ goto out;
+ }
+
+ m->ReturnValue = Iosb.Information;
+
+ st = NtWriteVirtualMemory(p->Process, args->Buf, IoBuffer,
+ args->Nbytes, NULL);
+ if (!NT_SUCCESS(st)) {
+ m->Error = PsxStatusToErrno(st);
+ }
+
+out:
+ st = NtFreeVirtualMemory(NtCurrentProcess(), &IoBuffer,
+ &IoBufferSize, MEM_RELEASE);
+ ASSERT(NT_SUCCESS(st));
+
+ return TRUE;
+}
+
+
+BOOLEAN
+FileDup (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd,
+ IN PFILEDESCRIPTOR FdDup
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements dup and dup2
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the request.
+
+ Fd - supplies the address of the file descriptor being duplicated.
+
+ FdDup - supplies the address of the duplicate file descriptor.
+
+Return Value:
+
+ ???
+--*/
+
+{
+ PPSX_DUP_MSG args;
+
+ args = &m->u.Dup;
+
+ //
+ // Copy contents of source file descriptor slot into new descriptor
+ // Note that FD_CLOEXEC must be CLEAR on FdDup.
+ //
+
+ *FdDup = *Fd;
+ FdDup->Flags &= ~PSX_FD_CLOSE_ON_EXEC;
+
+ //
+ // Increment reference count associated with the SystemOpenFile
+ // descriptor for this file.
+ //
+
+ // Grab system open file lock
+
+ RtlEnterCriticalSection(&SystemOpenFileLock);
+
+ Fd->SystemOpenFileDesc->HandleCount++;
+
+ RtlLeaveCriticalSection(&SystemOpenFileLock);
+
+ return TRUE;
+}
+
+
+BOOLEAN
+FileLseek (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements lseek when the device being seeked on
+ is a file.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the request.
+
+ Fd - supplies the address of the file descriptor being seekd
+
+Return Value:
+
+ ???
+
+--*/
+
+{
+ PPSX_LSEEK_MSG args;
+ NTSTATUS st;
+ IO_STATUS_BLOCK Iosb;
+ LARGE_INTEGER Offset, NewByteOffset;
+ FILE_POSITION_INFORMATION FilePosition;
+ FILE_STANDARD_INFORMATION StandardInfo;
+
+ args = &m->u.Lseek;
+
+ Offset = RtlConvertLongToLargeInteger(args->Offset);
+
+ switch (args->Whence) {
+
+ case SEEK_SET:
+ NewByteOffset = Offset;
+ break;
+
+ case SEEK_CUR:
+ st = NtQueryInformationFile(Fd->SystemOpenFileDesc->NtIoHandle,
+ &Iosb, &FilePosition, sizeof(FilePosition),
+ FilePositionInformation);
+ ASSERT(NT_SUCCESS(st));
+
+ NewByteOffset.QuadPart = Offset.QuadPart +
+ FilePosition.CurrentByteOffset.QuadPart;
+ break;
+
+ case SEEK_END:
+ st = NtQueryInformationFile(Fd->SystemOpenFileDesc->NtIoHandle,
+ &Iosb, &StandardInfo, sizeof(StandardInfo),
+ FileStandardInformation);
+ ASSERT(NT_SUCCESS(st));
+
+ NewByteOffset.QuadPart = Offset.QuadPart +
+ StandardInfo.EndOfFile.QuadPart;
+ break;
+
+ default:
+ m->Error = EINVAL;
+ return TRUE;
+ }
+
+ // Check for overflow. POSIX limited to arithmetic data type for off_t
+
+ if (NewByteOffset.HighPart != 0 || (off_t)NewByteOffset.LowPart < 0) {
+ m->Error = EINVAL;
+ return TRUE;
+ }
+
+ FilePosition.CurrentByteOffset = NewByteOffset;
+
+ args->Offset = NewByteOffset.LowPart;
+
+ st = NtSetInformationFile(Fd->SystemOpenFileDesc->NtIoHandle, &Iosb,
+ &FilePosition, sizeof(FilePosition), FilePositionInformation);
+
+ if (!NT_SUCCESS(st)) {
+ m->Error = EINVAL;
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+FileStat (
+ IN PIONODE IoNode,
+ IN HANDLE FileHandle,
+ OUT struct stat *StatBuf,
+ OUT NTSTATUS *pStatus
+ )
+/*++
+
+Routine Description:
+
+ This procedure implements stat when the device being read
+ is a file.
+
+Arguments:
+
+ IoNode - supplies a pointer to the ionode of the file for which stat is
+ requested. NULL if no active Ionode entry.
+
+ FileHandle - supplies the Nt file handle of the file .
+
+ StatBuf - Supplies the address of the statbuf portion of the message
+ associated with the request.
+
+Return Value:
+
+ ???
+
+--*/
+{
+ IO_STATUS_BLOCK Iosb;
+ FILE_INTERNAL_INFORMATION SerialNumber;
+ FILE_BASIC_INFORMATION BasicInfo;
+ FILE_STANDARD_INFORMATION StandardInfo;
+ ULONG PosixTime;
+ NTSTATUS st;
+
+ //
+ // First get the static information on the file from the ionode if
+ // there is one (i.e. if the file currently open.
+ // Open() sets the fields in the ionode.
+ //
+
+ if (NULL != IoNode) {
+ StatBuf->st_mode = IoNode->Mode;
+ StatBuf->st_ino = IoNode->FileSerialNumber;
+ StatBuf->st_dev = IoNode->DeviceSerialNumber;
+ StatBuf->st_uid = IoNode->OwnerId;
+ StatBuf->st_gid = IoNode->GroupId;
+
+ StatBuf->st_atime = IoNode->AccessDataTime;
+ StatBuf->st_ctime = IoNode->ModifyIoNodeTime;
+ StatBuf->st_mtime = IoNode->ModifyDataTime;
+ } else {
+ StatBuf->st_uid = 0;
+ StatBuf->st_gid = 0;
+
+ st = NtQueryInformationFile(FileHandle, &Iosb, &SerialNumber,
+ sizeof(SerialNumber), FileInternalInformation);
+ if (!NT_SUCCESS(st)) {
+ KdPrint(("PSXSS: NtQueryInfoFile failed: 0x%x\n", st));
+ *pStatus = st;
+ return TRUE;
+ }
+
+ st = NtQueryInformationFile(FileHandle, &Iosb, &BasicInfo,
+ sizeof(BasicInfo), FileBasicInformation);
+ if (!NT_SUCCESS(st)) {
+ //
+ // can return STATUS_NO_SUCH_FILE if network
+ // file system
+ //
+ *pStatus = st;
+ return TRUE;
+ }
+
+
+ StatBuf->st_ino = (ino_t)SerialNumber.IndexNumber.LowPart;
+ StatBuf->st_dev = 0;
+ StatBuf->st_mode = PsxDetermineFileClass(FileHandle);
+
+ FindOwnerModeFile(FileHandle, StatBuf);
+
+ // Convert Nt file times to POSIX ones
+ if (!RtlTimeToSecondsSince1970(&BasicInfo.LastAccessTime,
+ &PosixTime)) {
+ PosixTime = 0L;
+ }
+ StatBuf->st_atime = PosixTime;
+
+ if (!RtlTimeToSecondsSince1970(&BasicInfo.LastWriteTime,
+ &PosixTime)) {
+ PosixTime = 0L;
+ }
+ StatBuf->st_mtime = PosixTime;
+
+ if (!RtlTimeToSecondsSince1970(&BasicInfo.ChangeTime,
+ &PosixTime)) {
+ PosixTime = 0L;
+ }
+ StatBuf->st_ctime = PosixTime;
+ }
+
+ st = NtQueryInformationFile(FileHandle, &Iosb, &StandardInfo,
+ sizeof(StandardInfo), FileStandardInformation);
+ if (!NT_SUCCESS(st)) {
+ KdPrint(("PSXSS: NtQueryInfoFile(StdInfo): 0x%x\n",
+ st));
+ *pStatus = st;
+ return TRUE;
+ }
+
+ StatBuf->st_size = (off_t)StandardInfo.EndOfFile.LowPart;
+ StatBuf->st_nlink = StandardInfo.NumberOfLinks;
+
+ return TRUE;
+}
+
+void
+FindOwnerModeFile(
+ IN HANDLE FileHandle,
+ OUT struct stat *StatBuf
+ )
+{
+ SECURITY_INFORMATION SecurityInformation;
+ ULONG LengthNeeded;
+ PSID NtOwner, NtGroup;
+ BOOLEAN OwnerDefaulted, GroupDefaulted;
+ BOOLEAN AclPresent, AclDefaulted;
+ PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
+ PACL pAcl;
+ NTSTATUS st;
+ ACCESS_MASK UserAccess, GroupAccess, OtherAccess;
+
+ //
+ // Get the security descriptor for the file.
+ //
+ SecurityInformation = OWNER_SECURITY_INFORMATION |
+ GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
+
+ //
+ // First try a guess at the necessary descriptor size.
+ //
+
+ LengthNeeded = 2048;
+
+ SecurityDescriptor = RtlAllocateHeap(PsxHeap, 0, LengthNeeded);
+ if (NULL == SecurityDescriptor) {
+ return;
+ }
+
+ st = NtQuerySecurityObject(FileHandle, SecurityInformation,
+ SecurityDescriptor, LengthNeeded, &LengthNeeded);
+ if (STATUS_BUFFER_TOO_SMALL == st) {
+ RtlFreeHeap(PsxHeap, 0, (PVOID)SecurityDescriptor);
+ SecurityDescriptor = RtlAllocateHeap(PsxHeap, 0, LengthNeeded);
+ if (NULL == SecurityDescriptor) {
+ return;
+ }
+
+ st = NtQuerySecurityObject(FileHandle, SecurityInformation,
+ SecurityDescriptor, LengthNeeded, &LengthNeeded);
+ if (!NT_SUCCESS(st)) {
+ KdPrint(("PSXSS: FindOwnerModeFile: NtQsObj: 0x%x\n",
+ st));
+ RtlFreeHeap(PsxHeap, 0, (PVOID)SecurityDescriptor);
+ return;
+ }
+ } else if (!NT_SUCCESS(st)) {
+ return;
+ }
+
+ ASSERT(RtlValidSecurityDescriptor(SecurityDescriptor));
+
+ //
+ // Get the owner and group from the security descriptor
+ //
+
+ st = RtlGetOwnerSecurityDescriptor(SecurityDescriptor,
+ &NtOwner, &OwnerDefaulted);
+ if (!NT_SUCCESS(st)) {
+ RtlFreeHeap(PsxHeap, 0, (PVOID)SecurityDescriptor);
+ return;
+ }
+
+ st = RtlGetGroupSecurityDescriptor(SecurityDescriptor,
+ &NtGroup, &GroupDefaulted);
+ ASSERT(NT_SUCCESS(st));
+
+ if (NULL == NtOwner || NULL == NtGroup) {
+
+ //
+ // Seems like this file doesn't have an owner or a
+ // group. Would like to say that it's owned by 'world'
+ // or somesuch.
+ //
+ StatBuf->st_uid = 0;
+ StatBuf->st_gid = 0;
+
+ //
+ // Since we don't know who owns the file, we can't
+ // figure out what permissions we have on it. Say
+ // that all access is granted. We may be lying.
+ //
+
+ StatBuf->st_mode |= _S_PROT;
+
+ RtlFreeHeap(PsxHeap, 0, (PVOID)SecurityDescriptor);
+ return;
+ }
+
+ //
+ // Translate Nt uid and gid to Posix recognizable form
+ // and set StatBuf->st_uid and StatBuf->st_gid.
+ //
+
+ ASSERT(RtlValidSid(NtOwner));
+ ASSERT(RtlValidSid(NtGroup));
+
+ StatBuf->st_uid = MakePosixId(NtOwner);
+ StatBuf->st_gid = MakePosixId(NtGroup);
+
+ ASSERT(RtlValidSecurityDescriptor(SecurityDescriptor));
+
+ st = RtlGetDaclSecurityDescriptor(SecurityDescriptor,
+ &AclPresent, &pAcl, &AclDefaulted);
+ if (!NT_SUCCESS(st)) {
+ KdPrint(("PSXSS: RtlGetDaclSD: 0x%x\n", st));
+ }
+ ASSERT(NT_SUCCESS(st));
+
+ if (!AclPresent || (AclPresent && NULL == pAcl)) {
+ // All access is granted.
+
+ StatBuf->st_mode |= _S_PROT;
+ RtlFreeHeap(PsxHeap, 0, (PVOID)SecurityDescriptor);
+ return;
+ }
+
+ //
+ // We have a Dacl
+ //
+
+ ASSERT(RtlValidAcl(pAcl));
+
+ st = RtlInterpretPosixAcl(ACL_REVISION2, NtOwner, NtGroup,
+ pAcl, &UserAccess, &GroupAccess, &OtherAccess);
+ if (!NT_SUCCESS(st)) {
+ //
+ // XXX.mjb: The Acl is not a Posix acl. It might be nice to
+ // return an error or somesuch.
+ //
+ RtlFreeHeap(PsxHeap, 0, (PVOID)SecurityDescriptor);
+ return;
+ }
+
+ RtlFreeHeap(PsxHeap, 0, (PVOID)SecurityDescriptor);
+ StatBuf->st_mode |= AccessMaskToMode(UserAccess, GroupAccess,
+ OtherAccess);
+}
diff --git a/private/posix/psxss/flocks.c b/private/posix/psxss/flocks.c
new file mode 100644
index 000000000..de2e3f859
--- /dev/null
+++ b/private/posix/psxss/flocks.c
@@ -0,0 +1,954 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ flocks.c
+
+Abstract:
+
+ Implementation of Posix advisory file record locking (via fcntl).
+
+Author:
+
+ Matthew Bradburn (mattbr) 11-May-1992
+
+Revision History:
+
+--*/
+
+#include <fcntl.h>
+#include "psxsrv.h"
+#include "psxmsg.h"
+
+void
+InsertFlock(
+ PIONODE IoNode,
+ PSYSFLOCK l
+ );
+
+void
+WakeAllFlockers(
+ PIONODE IoNode
+ );
+
+int
+found_region(
+ PPSX_PROCESS Proc,
+ PIONODE IoNode,
+ int command,
+ PSYSFLOCK list,
+ struct flock *new,
+ off_t *new_off,
+ ULONG FileLength,
+ OUT int *error
+ );
+
+VOID
+FlockHandler(
+ IN PPSX_PROCESS p,
+ IN PINTCB IntControlBlock,
+ IN PSX_INTERRUPTREASON InterruptReason,
+ IN int Signal
+ );
+
+//
+// These macros take care of the notion that if the length is
+// 0, it means the lock goes until EOF
+//
+
+#define LEN_A(a) ((a)->l_len == 0 ? (off_t)(FileLength - abs_off) : (a)->l_len)
+#define LEN_B(b) ((b)->Len == 0 ? (off_t)(FileLength - (b)->Start) : (b)->Len)
+
+#define OVERLAP(a, b) \
+ ((abs_off >= (b)->Start && \
+ abs_off < (b)->Start + LEN_B(b)) \
+ || (abs_off + LEN_A(a) >= (b)->Start && \
+ abs_off + LEN_A(a) <= (b)->Start + LEN_B(b)) \
+ || (abs_off <= (b)->Start && \
+ abs_off + LEN_A(a) >= (b)->Start + LEN_B(b)))
+
+
+//
+// Return values from found_region
+//
+#define DONE 1
+#define CONTINUE 2
+#define WAIT 3
+#define ERROR 4
+
+//
+// DoFlockStuff -- do fcntl commands related to advisory file-record
+// locking.
+//
+// Locking: the caller holds the IoNode locked throughout.
+//
+// Returns: TRUE if the caller should reply to the api request, FALSE
+// if the process has been blocked (F_SETLKW) and no reply
+// should be sent.
+//
+
+BOOLEAN
+DoFlockStuff(
+ PPSX_PROCESS Proc,
+ PPSX_API_MSG m,
+ int command,
+ IN PFILEDESCRIPTOR Fd,
+ IN OUT struct flock *new,
+ OUT int *error)
+{
+ PSYSFLOCK p;
+ PSYSFLOCK l;
+ off_t abs_off;
+ NTSTATUS Status;
+ IO_STATUS_BLOCK IoStat;
+ FILE_POSITION_INFORMATION FilePosition;
+ FILE_STANDARD_INFORMATION FileStandardInfo;
+ PIONODE IoNode;
+ int did_find_overlap = 0; // were changes made?
+ ULONG FileLength;
+
+ if (new->l_start < 0) {
+ *error = EINVAL;
+ return TRUE;
+ }
+ if (new->l_type < F_RDLCK || new->l_type > F_WRLCK) {
+ *error = EINVAL;
+ return TRUE;
+ }
+
+ IoNode = Fd->SystemOpenFileDesc->IoNode;
+
+ Status = NtQueryInformationFile(
+ Fd->SystemOpenFileDesc->NtIoHandle, &IoStat,
+ &FileStandardInfo, sizeof(FileStandardInfo),
+ FileStandardInformation);
+
+ if (!NT_SUCCESS(Status)) {
+ *error = ENOMEM;
+ return TRUE;
+ }
+
+ ASSERT(0 == FileStandardInfo.EndOfFile.HighPart);
+
+ FileLength = FileStandardInfo.EndOfFile.LowPart;
+
+ //
+ // Convert l_whence and l_start into the absolute position of the
+ // start of the region described.
+ //
+
+ switch (new->l_whence) {
+ case SEEK_SET:
+ abs_off = 0L;
+ break;
+ case SEEK_CUR:
+ Status = NtQueryInformationFile(
+ Fd->SystemOpenFileDesc->NtIoHandle, &IoStat,
+ &FilePosition, sizeof(FilePosition),
+ FilePositionInformation);
+ ASSERT(NT_SUCCESS(Status));
+ ASSERT(0 == FilePosition.CurrentByteOffset.HighPart);
+ abs_off = FilePosition.CurrentByteOffset.LowPart;
+ break;
+ case SEEK_END:
+ abs_off = FileLength;
+ break;
+ default:
+ *error = EINVAL;
+ return TRUE;
+ }
+ abs_off += new->l_start;
+
+ //
+ // If we're actually trying to modify an already-locked region
+ // or create an additional locked, region, we scan the list of
+ // locks to see whether we can succeed. If we can, we go through
+ // the list a second time and perform the action.
+ //
+
+ if ((F_SETLK == command || F_SETLKW == command)
+ && (F_UNLCK != new->l_type)) {
+
+ //
+ // If the regions overlap, they must either both
+ // be of type F_RDLCK, or they must have the same
+ // owner. Otherwise we do not succeed (may block).
+ //
+ for (p = (PSYSFLOCK)IoNode->Flocks.Flink;
+ p != (PSYSFLOCK)&IoNode->Flocks;
+ p = (PSYSFLOCK)p->Links.Flink) {
+ if (!OVERLAP(new, p)) {
+ continue;
+ }
+ if ((F_RDLCK == new->l_type && F_RDLCK == p->Type)
+ || (Proc->Pid == p->Pid)) {
+ continue;
+ }
+ if (F_SETLK == command) {
+ *error = EAGAIN;
+ return TRUE;
+ }
+ ASSERT(F_SETLKW == command);
+
+ //
+ // BLOCK the process.
+ //
+ Status = BlockProcess(Proc, Proc, FlockHandler, m,
+ &IoNode->Waiters, &IoNode->IoNodeLock);
+ if (!NT_SUCCESS(Status)) {
+ m->Error = PsxStatusToErrno(Status);
+ return TRUE;
+ }
+
+ //
+ // Successfully blocked -- don't reply to api request.
+ //
+ return FALSE;
+ }
+ }
+
+ //
+ // Traverse the list of flocks, looking for locks describing
+ // regions that overlap the given lock. Peform the indicated
+ // operation on each.
+ //
+
+ p = (PVOID)IoNode->Flocks.Flink;
+
+ while (p != (PVOID)&IoNode->Flocks) {
+
+ //
+ // Get the "next" pointer here, so that if found_region
+ // frees the entry pointed to by p we'll still be able to
+ // traverse the list.
+ //
+
+ l = (PVOID)p->Links.Flink;
+
+ //
+ // Find out whether the regions overlap or not: does the
+ // new region start in this region, or does it end in this
+ // region?
+ //
+
+ if (OVERLAP(new, p)) {
+ if ((F_SETLK == command || F_SETLKW == command) &&
+ F_UNLCK == new->l_type) {
+
+ //
+ // The process is only allowed to unlock locks
+ // that he owns.
+ //
+
+ if (p->Pid != Proc->Pid) {
+ continue;
+ }
+ }
+ did_find_overlap++;
+ switch (found_region(Proc, IoNode, command, p,
+ new, &abs_off, FileLength, error)) {
+ case DONE:
+ *error = 0;
+ return TRUE;
+ case CONTINUE:
+ p = l;
+ continue;
+ case ERROR:
+ return TRUE;
+ default:
+ ASSERT(0);
+ }
+ }
+ if (p->Start > abs_off) {
+ break;
+ }
+
+ p = l;
+ }
+
+ //
+ // we have come to the end of the list, or we have come to a
+ // place that our region should have been before.
+ //
+
+ if (F_GETLK == command) {
+ // no such region; the lock type is set to
+ // UNLOCK.
+ new->l_type = F_UNLCK;
+ *error = 0;
+ return TRUE;
+ }
+ if ((F_SETLK == command || F_SETLKW == command) &&
+ F_UNLCK == new->l_type) {
+
+ //
+ // Return an error only if nothing was found to unlock.
+ // We do this to allow the user to give a single unlock
+ // command spanning the entire file, this to unlock all his
+ // locks. 1003.1-90 isn't really clear on how this stuff
+ // should work.
+ //
+
+ if (!did_find_overlap) {
+ *error = EINVAL;
+ return TRUE;
+ }
+
+ WakeAllFlockers(IoNode);
+
+ *error = 0;
+ return TRUE;
+ }
+
+ // We're setting a lock, and the type is not UNLOCK.
+ // So insert a new region here.
+
+ l = RtlAllocateHeap(PsxHeap, 0, sizeof(*l));
+ if (NULL == l) {
+ *error = ENOMEM;
+ return TRUE;
+ }
+ l->Start = abs_off;
+ l->Len = new->l_len;
+ l->Pid = Proc->Pid;
+ l->Type = new->l_type;
+
+ InsertFlock(IoNode, l);
+ *error = 0;
+ return TRUE;
+}
+
+//
+// found_region -- this gets called when there is a region overlapping the
+// one the user is interested in. Actually, the user's request may
+// span several regions, in which case this routine will get called
+// for each one.
+//
+
+int
+found_region(
+ PPSX_PROCESS Proc, // the process making the request
+ PIONODE IoNode, // our IoNode.
+ int command, // what to do
+ PSYSFLOCK list, // region that matches new region
+ struct flock *new, // new region
+ IN OUT off_t *new_off, // absolute offset, start of new region
+ ULONG FileLength, // current length of the file
+ OUT int *error // returned errno
+ )
+{
+ off_t abs_off = *new_off;
+
+ if (F_GETLK == command) {
+
+ //
+ // If we own the overlapping region, we wouldn't block on
+ // it. So we must continue searching.
+ //
+
+ if (list->Pid == Proc->Pid) {
+ return CONTINUE;
+ }
+
+ //
+ // If both lock types are F_RDLCK, then we have not
+ // yet found a lock the user would block against; we
+ // must continue searching.
+ //
+
+ if (new->l_type == F_RDLCK && list->Type == F_RDLCK) {
+ return CONTINUE;
+ }
+
+ new->l_pid = list->Pid;
+ new->l_type = list->Type;
+ new->l_whence = SEEK_SET;
+ new->l_start = list->Start;
+ new->l_len = list->Len;
+ return DONE;
+ }
+
+ if (F_SETLK != command && F_SETLKW != command) {
+ KdPrint(("PSXSS: found_region: command %d\n", command));
+ }
+ ASSERT(F_SETLK == command || F_SETLKW == command);
+
+ //
+ // If this user doesn't own the found region, and they're both
+ // read locks, we just add a shared lock.
+ //
+
+ if (list->Pid != Proc->Pid &&
+ F_RDLCK == list->Type && F_RDLCK == new->l_type) {
+ PSYSFLOCK l;
+
+ // set a shared lock.
+ l = RtlAllocateHeap(PsxHeap, 0, sizeof(*l));
+ if (NULL == l) {
+ *error = ENOMEM;
+ return ERROR;
+ }
+ l->Start = *new_off;
+ l->Len = new->l_len;
+ l->Type = new->l_type;
+ l->Pid = Proc->Pid;
+ InsertFlock(IoNode, l);
+ return DONE;
+ }
+
+ //
+ // The only way we should get here is if we're unlocking a region
+ // or if we are changing the type of a region we own. In each case
+ // we should own the region.
+ //
+ ASSERT(list->Pid == Proc->Pid);
+
+ if (*new_off == list->Start && LEN_A(new) == LEN_B(list)) {
+
+ //
+ // The found region exactly matches the region in
+ // the argument.
+ //
+
+ if (F_UNLCK == new->l_type) {
+ PSYSFLOCK p;
+
+ RemoveEntryList(&list->Links);
+ RtlFreeHeap(PsxHeap, 0, list);
+
+ //
+ // Wake all procs sleeping on this IoNode.
+ //
+
+ WakeAllFlockers(IoNode);
+
+ return DONE;
+ }
+
+ list->Type = new->l_type;
+ return DONE;
+ }
+
+ if (*new_off == list->Start && LEN_A(new) < LEN_B(list)) {
+ PSYSFLOCK l;
+
+ //
+ // The new region starts at the same place as the found
+ // region, but does not extend all the way to its end.
+ //
+
+ if (F_UNLCK == new->l_type) {
+ list->Start += LEN_A(new);
+ list->Len = LEN_B(list);
+ list->Len -= LEN_A(new);
+
+ //
+ // We've changed this entry's starting point, so
+ // we may have changed it's order in the list.
+ //
+ RemoveEntryList(&list->Links);
+ InsertFlock(IoNode, list);
+ return DONE;
+ }
+
+ //
+ // Change the type. The region gets split in
+ // two.
+ //
+
+ l = RtlAllocateHeap(PsxHeap, 0, sizeof(*l));
+ if (NULL == l) {
+ *error = ENOMEM;
+ return ERROR;
+ }
+ l->Start = *new_off;
+ l->Len = LEN_A(new);
+ l->Type = new->l_type;
+ l->Pid = Proc->Pid;
+ InsertFlock(IoNode, l);
+
+ list->Start += new->l_len;
+ list->Len = LEN_B(list);
+ list->Len -= LEN_A(new);
+
+ // re-sort
+ RemoveEntryList(&list->Links);
+ InsertFlock(IoNode, list);
+ return DONE;
+ }
+ if (*new_off == list->Start && LEN_A(new) > LEN_B(list)) {
+ //
+ // The new region starts at the same place as the found
+ // region, but extends beyond it, perhaps into some number
+ // of additional regions
+ //
+
+ if (F_UNLCK == new->l_type) {
+
+ //
+ // We are unlocking all of this region and maybe
+ // some other stuff, too.
+ //
+
+ RemoveEntryList(&list->Links);
+ RtlFreeHeap(PsxHeap, 0, list);
+
+ return CONTINUE;
+ }
+
+ // Just change the type.
+
+ list->Type = new->l_type;
+ new->l_start += list->Len;
+ *new_off = new->l_start;
+ new->l_len = LEN_A(new);
+ new->l_len -= LEN_B(list);
+
+ return CONTINUE;
+ }
+ if (*new_off > list->Start &&
+ *new_off + LEN_A(new) == list->Start + LEN_B(list)) {
+ PSYSFLOCK l;
+
+ //
+ // The new region starts inside the found one and extends
+ // to its end.
+ //
+
+ if (F_UNLCK == new->l_type) {
+ ASSERT(list->Pid == Proc->Pid);
+
+ // unlocking the last portion of a region
+
+ list->Len = *new_off - list->Start;
+ return DONE;
+ }
+
+ //
+ // need to split the region.
+ //
+
+ list->Len = *new_off - list->Start;
+
+ l = RtlAllocateHeap(PsxHeap, 0, sizeof(*l));
+ if (NULL == l) {
+ *error = ENOMEM;
+ return ERROR;
+ }
+ l->Start = *new_off;
+ l->Len = LEN_A(new);
+ l->Pid = Proc->Pid;
+ l->Type = new->l_type;
+ InsertFlock(IoNode, l);
+
+ return DONE;
+ }
+ if (*new_off > list->Start &&
+ *new_off + LEN_A(new) < list->Start + LEN_B(list)) {
+ PSYSFLOCK l;
+
+ //
+ // The new region starts inside the found region and does
+ // not extend to its end.
+ //
+
+ if (F_UNLCK == new->l_type) {
+
+ //
+ // The list region gets split into two.
+ // Change the list region to be the left
+ // side, and add another region for the right side.
+ //
+
+ l = RtlAllocateHeap(PsxHeap, 0, sizeof(*l));
+ if (NULL == l) {
+ *error = ENOMEM;
+ return ERROR;
+ }
+ l->Start = *new_off + LEN_A(new);
+ l->Len = (list->Start + LEN_B(list)) -
+ (*new_off + LEN_A(new));
+ l->Type = list->Type;
+ l->Pid = Proc->Pid;
+
+ InsertFlock(IoNode, l);
+
+ // list->Start stays the same;
+ list->Len = *new_off - list->Start;
+
+ return DONE;
+ }
+
+ //
+ // changing the type of a sub-region; we end up
+ // splitting the region into three.
+ //
+
+ // the right region
+ l = RtlAllocateHeap(PsxHeap, 0, sizeof(*l));
+ if (NULL == l) {
+ *error = ENOMEM;
+ return ERROR;
+ }
+ l->Start = *new_off + LEN_A(new);
+ l->Len = (list->Start + LEN_B(list)) - (*new_off + LEN_A(new));
+ l->Pid = Proc->Pid;
+ l->Type = list->Type;
+ InsertFlock(IoNode, l);
+
+ // the left region
+ list->Len = *new_off - list->Start;
+ // list->Start stays the same
+
+ // the center region
+ l = RtlAllocateHeap(PsxHeap, 0, sizeof(*l));
+ if (NULL == l) {
+ *error = ENOMEM;
+ return ERROR;
+ }
+ l->Start = *new_off;
+ l->Len = LEN_A(new);
+ l->Type = new->l_type;
+ l->Pid = Proc->Pid;
+ InsertFlock(IoNode, l);
+
+ return DONE;
+ }
+
+ if (*new_off > list->Start &&
+ *new_off + LEN_A(new) > list->Start + LEN_B(list)) {
+ PSYSFLOCK l;
+
+ //
+ // The new region starts in the found region and extends
+ // beyond it.
+ //
+
+ if (F_UNLCK == new->l_type) {
+
+ new->l_start = list->Start + LEN_B(list) + 1;
+ list->Len = *new_off - list->Start;
+ new->l_whence = SEEK_SET;
+ new->l_len = LEN_A(new) - (new->l_start - *new_off);
+ *new_off = new->l_start;
+
+ return CONTINUE;
+ }
+
+ //
+ // changing the lock type of a region starting in this
+ // region, presumably extending into the next region.
+ // this involves creating a new region for the change
+ // and continuing on.
+ //
+
+ l = RtlAllocateHeap(PsxHeap, 0, sizeof(*l));
+ if (NULL == l) {
+ *error = ENOMEM;
+ return ERROR;
+ }
+ l->Start = *new_off;
+ l->Whence = SEEK_SET;
+ l->Len = (list->Start + LEN_B(list)) - *new_off;
+ l->Pid = Proc->Pid;
+ l->Type = new->l_type;
+ InsertFlock(IoNode, l);
+
+ new->l_start = list->Start + LEN_B(list);
+ new->l_whence = SEEK_SET;
+ new->l_len = LEN_A(new) - (new->l_start - *new_off);
+ *new_off = new->l_start;
+
+ list->Len = l->Start - list->Start;
+
+ return CONTINUE;
+ }
+ if (*new_off < list->Start &&
+ *new_off + LEN_A(new) < list->Start + LEN_B(list)) {
+ off_t new_end = *new_off + (LEN_A(new) - 1);
+ PSYSFLOCK l;
+
+ //
+ // The new region starts before the list region and ends
+ // in the midst of it.
+ //
+
+ if (F_UNLCK == new->l_type) {
+
+ list->Len = (list->Start + LEN_B(list) - 1) - new_end;
+ list->Start = new_end + 1;
+
+ return DONE;
+ }
+
+ //
+ // The list region gets split into two.
+ //
+
+ l = RtlAllocateHeap(PsxHeap, 0, sizeof(*l));
+ if (NULL == l) {
+ return ERROR;
+ }
+ l->Start = *new_off;
+ l->Len = LEN_A(new);
+ l->Type = new->l_type;
+ l->Whence = SEEK_SET;
+ l->Pid = Proc->Pid;
+ InsertFlock(IoNode, l);
+
+ list->Len = (list->Start + LEN_B(list) - 1) - new_end;
+ list->Start = new_end + 1;
+
+ return DONE;
+ }
+ if (*new_off < list->Start &&
+ *new_off + LEN_A(new) == list->Start + LEN_B(list)) {
+
+ //
+ // The new region starts before the list region and
+ // ends at the same place.
+ //
+
+ if (F_UNLCK == new->l_type) {
+ RemoveEntryList(&list->Links);
+ RtlFreeHeap(PsxHeap, 0, list);
+ return DONE;
+ }
+
+ list->Start = *new_off;
+ list->Type = new->l_type;
+ list->Len = LEN_A(new);
+ return DONE;
+ }
+ if (*new_off < list->Start &&
+ *new_off + LEN_A(new) > list->Start + LEN_B(list)) {
+ //
+ // The new region starts before the list region and
+ // ends after it.
+ //
+
+ if (F_UNLCK == new->l_type) {
+ //
+ // there may be stuff still to unlock when we're
+ // done.
+ //
+
+ new->l_start = *new_off + LEN_A(new) + 1;
+ new->l_len = (*new_off + LEN_A(new)) -
+ (list->Start + LEN_B(list));
+ new->l_whence = SEEK_SET;
+
+ RemoveEntryList(&list->Links);
+ RtlFreeHeap(PsxHeap, 0, list);
+
+ return CONTINUE;
+ }
+
+ //
+ // The new lock subsumes the one we've found on
+ // the list.
+ //
+
+ RemoveEntryList(&list->Links);
+ RtlFreeHeap(PsxHeap, 0, list);
+ return CONTINUE;
+ }
+
+ ASSERT(0); // shouldn't get here.
+}
+
+
+void
+ReleaseFlocksByPid(
+ PIONODE IoNode,
+ pid_t pid
+ )
+{
+ PSYSFLOCK p;
+ PINTCB IntCb;
+ PPSX_PROCESS Waiter;
+ PLIST_ENTRY next;
+
+ //
+ // Remove all the locks that we hold.
+ //
+
+ for (p = CONTAINING_RECORD(IoNode->Flocks.Flink, SYSFLOCK, Links);
+ p != (PVOID)&IoNode->Flocks;
+ /* null */) {
+
+ PSYSFLOCK next;
+
+ next = CONTAINING_RECORD(p->Links.Flink, SYSFLOCK, Links);
+
+ if (pid == p->Pid) {
+ RemoveEntryList(&p->Links);
+ RtlFreeHeap(PsxHeap, 0, (PVOID)p);
+ }
+
+ p = next;
+ }
+
+ //
+ // Wake all procs sleeping on this IoNode; since we've released
+ // all flocks belonging to the given pid, maybe one of the
+ // blocked procs can run.
+ //
+
+ WakeAllFlockers(IoNode);
+}
+
+#if DBG
+void
+DumpFlockList(PIONODE IoNode)
+{
+ PSYSFLOCK p;
+ int i;
+
+ KdPrint(("DumpFlockList:\n"));
+
+ for (i = 0, p = (PSYSFLOCK)IoNode->Flocks.Flink;
+ p != (PSYSFLOCK)&IoNode->Flocks;
+ p = (PSYSFLOCK)p->Links.Flink, ++i) {
+ KdPrint(("Flock %d:\n", i));
+ KdPrint(("\t Start %d\n", p->Start));
+ KdPrint(("\t Length %d\n", p->Len));
+ KdPrint(("\t Type %d\n", p->Type));
+ KdPrint(("\t Pid 0x%x\n", p->Pid));
+ }
+
+}
+#endif
+
+//
+// InsertFlock -- insert the flock in the list. The list is ordered
+// according to the starting offset.
+//
+//
+VOID
+InsertFlock(
+ PIONODE IoNode,
+ PSYSFLOCK l
+ )
+{
+ PSYSFLOCK p;
+
+ ASSERT(NULL != l);
+
+ for (p = CONTAINING_RECORD(IoNode->Flocks.Flink, SYSFLOCK, Links);
+ p != (PVOID)&IoNode->Flocks;
+ p = CONTAINING_RECORD(p->Links.Flink, SYSFLOCK, Links)) {
+
+ if (p->Start >= l->Start) {
+ InsertTailList(&p->Links, &l->Links);
+ return;
+ }
+ }
+
+ InsertTailList(&IoNode->Flocks, &l->Links);
+}
+
+VOID
+WakeAllFlockers(
+ PIONODE IoNode
+ )
+{
+ PINTCB IntCb;
+ PPSX_PROCESS Waiter;
+ PVOID p, prev;
+
+ RtlEnterCriticalSection(&BlockLock);
+
+ //
+ // We go through the list of blocked processes and wake them
+ // all up. Each calls the FlockHandler routine below. This is
+ // a terrible kludge: we go through the list from end to beginning
+ // because if any of the processed we've waked are added to the
+ // list, they get added at the end (BlockProcess() calls Insert-
+ // TailList()).
+ //
+
+ for (p = (PVOID)IoNode->Waiters.Blink;
+ p != (PVOID)&IoNode->Waiters;
+ p = prev) {
+
+ ASSERT(NULL != p);
+
+ IntCb = CONTAINING_RECORD((PINTCB)p, INTCB, Links);
+ ASSERT(NULL != IntCb);
+
+ Waiter = (PPSX_PROCESS)IntCb->IntContext;
+ ASSERT(NULL != Waiter);
+
+ prev = IntCb->Links.Blink;
+
+ UnblockProcess(Waiter, WaitSatisfyInterrupt,
+ TRUE, 0);
+
+ RtlEnterCriticalSection(&BlockLock);
+ }
+
+ RtlLeaveCriticalSection(&BlockLock);
+}
+
+
+VOID
+FlockHandler(
+ IN PPSX_PROCESS p,
+ IN PINTCB IntControlBlock,
+ IN PSX_INTERRUPTREASON InterruptReason,
+ IN int Signal // signal causing wakeup, if any
+ )
+/*++
+
+Routine Description:
+
+ This procedure is called when a process releases a lock.
+
+Arguments:
+
+ p - Supplies the address of the sleeping process.
+
+ IntControlBlock - Supplies the address of the interrupt control
+ block.
+
+ InterruptReason - Why the sleep is being interrupted.
+
+Return value:
+
+ None.
+
+--*/
+{
+ PPSX_API_MSG m;
+ PPSX_FCNTL_MSG args;
+
+ RtlLeaveCriticalSection(&BlockLock);
+
+ m = IntControlBlock->IntMessage;
+
+ if (InterruptReason == SignalInterrupt) {
+ //
+ // The sleep is being interrupted by a signal. We return
+ // EINTR to the Posix process.
+ //
+
+ RtlFreeHeap(PsxHeap, 0, (PVOID)IntControlBlock);
+ m->Error = EINTR;
+ m->Signal = Signal;
+ ApiReply(p, m, NULL);
+ RtlFreeHeap(PsxHeap, 0, (PVOID)m);
+ return;
+ }
+
+ args = &m->u.Fcntl;
+
+ RtlFreeHeap(PsxHeap, 0, (PVOID)IntControlBlock);
+
+ if (PsxFcntl(p, m)) {
+ ApiReply(p, m, NULL);
+ }
+ RtlFreeHeap(PsxHeap, 0, m);
+}
diff --git a/private/posix/psxss/lpipeio.c b/private/posix/psxss/lpipeio.c
new file mode 100644
index 000000000..d338acca7
--- /dev/null
+++ b/private/posix/psxss/lpipeio.c
@@ -0,0 +1,1322 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ lpipeio.c
+
+Abstract:
+
+ This module implements all file descriptor oriented APIs.
+
+Author:
+
+ Mark Lucovsky (markl) 30-Mar-1989
+
+Revision History:
+
+--*/
+
+
+#include <sys/stat.h>
+#include "psxsrv.h"
+
+BOOLEAN
+LocalPipeRead (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd
+ );
+
+BOOLEAN
+LocalPipeWrite (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd
+ );
+
+BOOLEAN
+LocalPipeDup(
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd,
+ IN PFILEDESCRIPTOR FdDup
+ );
+
+BOOLEAN
+LocalPipeLseek (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements lseek when the device being seeked on
+ is a local or named pipe.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the request.
+
+ Fd - supplies the address of the file descriptor being seekd
+
+Return Value:
+
+ ???
+
+--*/
+
+{
+ m->Error = ESPIPE;
+ return TRUE;
+}
+
+BOOLEAN
+LocalPipeStat (
+ IN PIONODE IoNode,
+ IN HANDLE FileHandle,
+ OUT struct stat *StatBuf,
+ OUT NTSTATUS *pStatus
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements stat when the device being read
+ is a local pipe.
+
+Arguments:
+
+ IoNode - supplies a pointer to the ionode of the pipe for which stat is
+ requested.
+
+ FileHandle - supplies the Nt file handle of the pipe. NULL for local pipes.
+
+ StatBuf - Supplies the address of the statbuf portion of the message
+ associated with the request.
+
+Return Value:
+
+ ???
+
+--*/
+{
+ // Pipe() sets the IoNode fields.
+
+ StatBuf->st_mode = IoNode->Mode;
+ StatBuf->st_ino = IoNode->FileSerialNumber;
+ StatBuf->st_dev = IoNode->DeviceSerialNumber;
+ StatBuf->st_uid = IoNode->OwnerId;
+ StatBuf->st_gid = IoNode->GroupId;
+
+ StatBuf->st_atime = IoNode->AccessDataTime;
+ StatBuf->st_mtime = IoNode->ModifyDataTime;
+ StatBuf->st_ctime = IoNode->ModifyIoNodeTime;
+
+ StatBuf->st_size = PIPE_BUF;
+
+ // This implementation dependent.
+ StatBuf->st_nlink = 0;
+
+ return TRUE;
+}
+
+
+VOID
+LocalPipeNewHandle (
+ IN PPSX_PROCESS p,
+ IN PFILEDESCRIPTOR Fd
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called any time a handle is created for a pipe.
+
+Arguments:
+
+ p - Supplies a pointer to the process creating the handle to the pipe.
+
+ Fd - Supplies the file descriptor that refers to the pipe.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PLOCAL_PIPE Pipe;
+
+ Pipe = (PLOCAL_PIPE)Fd->SystemOpenFileDesc->IoNode->Context;
+
+ RtlEnterCriticalSection(&Pipe->CriticalSection);
+
+ if (Fd->SystemOpenFileDesc->Flags & PSX_FD_READ) {
+ Pipe->ReadHandleCount++;
+ }
+
+ if (Fd->SystemOpenFileDesc->Flags & PSX_FD_WRITE) {
+ Pipe->WriteHandleCount++;
+ }
+
+ RtlLeaveCriticalSection(&Pipe->CriticalSection);
+}
+
+VOID
+LocalPipeClose (
+ IN PPSX_PROCESS p,
+ IN PFILEDESCRIPTOR Fd
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called any time a handle is deleted for a pipe.
+
+Arguments:
+
+ p - Supplies a pointer to the closing the handle to the pipe.
+
+ Fd - Supplies the file descriptor that refers to the pipe.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PLOCAL_PIPE Pipe;
+ PINTCB IntCb;
+ PPSX_PROCESS WaitingProc;
+ PLIST_ENTRY Next;
+
+ Pipe = (PLOCAL_PIPE)Fd->SystemOpenFileDesc->IoNode->Context;
+
+ RtlEnterCriticalSection(&Pipe->CriticalSection);
+
+ if ((Fd->SystemOpenFileDesc->Flags & PSX_FD_READ) &&
+ (0 == --Pipe->ReadHandleCount)) {
+
+ //
+ // Last reader close; any writers hanging around
+ // get EPIPE and a SIGPIPE
+ //
+
+ RtlEnterCriticalSection(&BlockLock);
+
+ Next = Pipe->WaitingWriters.Flink;
+ while (Next != &Pipe->WaitingWriters) {
+ IntCb = CONTAINING_RECORD(Next, INTCB, Links);
+
+ WaitingProc = (PPSX_PROCESS)IntCb->IntContext;
+ UnblockProcess(WaitingProc, IoCompletionInterrupt,
+ TRUE, 0);
+ RtlEnterCriticalSection(&BlockLock);
+
+ Next = Pipe->WaitingWriters.Flink;
+ }
+
+ RtlLeaveCriticalSection(&BlockLock);
+ }
+
+ if ((Fd->SystemOpenFileDesc->Flags & PSX_FD_WRITE) &&
+ (0 == --Pipe->WriteHandleCount)) {
+
+ //
+ // Last writer close; any readers hanging around
+ // get 0.
+ //
+
+ RtlEnterCriticalSection(&BlockLock);
+
+ Next = Pipe->WaitingReaders.Flink;
+ while (Next != &Pipe->WaitingReaders) {
+ IntCb = CONTAINING_RECORD(Next, INTCB, Links);
+
+ WaitingProc = (PPSX_PROCESS)IntCb->IntContext;
+ UnblockProcess(WaitingProc, IoCompletionInterrupt,
+ TRUE, 0);
+ RtlEnterCriticalSection(&BlockLock);
+
+ Next = Pipe->WaitingReaders.Flink;
+ }
+
+ RtlLeaveCriticalSection(&BlockLock);
+ }
+ RtlLeaveCriticalSection(&Pipe->CriticalSection);
+}
+
+
+VOID
+LocalPipeIoNodeClose (
+ IN PIONODE IoNode
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called when the IONODE representing a pipe is
+ closed. Its function is to tear down the pipe.
+
+Arguments:
+
+ IoNode - Supplies the IoNode being deleted
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PLOCAL_PIPE Pipe;
+
+ Pipe = (PLOCAL_PIPE) IoNode->Context;
+
+ RtlDeleteCriticalSection(&Pipe->CriticalSection);
+
+ RtlFreeHeap(PsxHeap, 0,Pipe);
+
+}
+
+
+PSXIO_VECTORS LocalPipeVectors = {
+ NULL,
+ LocalPipeNewHandle,
+ LocalPipeClose,
+ NULL,
+ LocalPipeIoNodeClose,
+ LocalPipeRead,
+ LocalPipeWrite,
+ LocalPipeDup,
+ LocalPipeLseek,
+ LocalPipeStat
+ };
+
+VOID
+InitializeLocalPipe(
+ IN PLOCAL_PIPE Pipe
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes a local pipe
+
+Arguments:
+
+ Pipe - Supplies the address of a local pipe
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS st;
+
+ st = RtlInitializeCriticalSection(&Pipe->CriticalSection);
+ ASSERT(NT_SUCCESS(st));
+
+ InitializeListHead(&Pipe->WaitingWriters);
+ InitializeListHead(&Pipe->WaitingReaders);
+
+ Pipe->ReadHandleCount = 0;
+ Pipe->WriteHandleCount = 0;
+ Pipe->BufferSize = PIPE_BUF;
+ Pipe->DataInPipe = 0;
+ Pipe->WritePointer = &Pipe->Buffer[0];
+ Pipe->ReadPointer = &Pipe->Buffer[0];
+}
+
+VOID
+LocalPipeWriteHandler(
+ IN PPSX_PROCESS p,
+ IN PINTCB IntControlBlock,
+ IN PSX_INTERRUPTREASON InterruptReason,
+ IN int Signal // signal causing wakeup, if any
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure is called when a there is room in a pipe, and a blocked
+ writer exists whose current write request length is less than the
+ amount of room in the pipe.
+
+Arguments:
+
+ p - Supplies the address of the process being interrupted.
+
+ IntControlBlock - Supplies the address of the interrupt control block.
+
+ InterruptReason - Supplies the reason that this process is being
+ interrupted.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PFILEDESCRIPTOR Fd;
+ BOOLEAN reply;
+ PPSX_API_MSG m;
+ PPSX_WRITE_MSG args;
+
+ RtlLeaveCriticalSection(&BlockLock);
+
+ m = IntControlBlock->IntMessage;
+ args = &m->u.Write;
+
+ if (InterruptReason == SignalInterrupt) {
+
+ //
+ // The write was interrupted by a signal. Bail out of
+ // service and let the interrupt be handled
+ //
+
+ RtlFreeHeap(PsxHeap, 0,IntControlBlock);
+
+ m->Error = EINTR;
+ m->Signal = Signal;
+
+ ApiReply(p,m,NULL);
+ RtlFreeHeap(PsxHeap, 0,m);
+ return;
+ }
+
+ Fd = FdIndexToFd(p,args->FileDes);
+
+ if (!Fd) {
+ Panic("LocalPipeWriteHandler: FdIndex");
+ }
+
+ RtlFreeHeap(PsxHeap, 0, IntControlBlock);
+
+ reply = LocalPipeWrite(p, m, Fd);
+
+ if (reply) {
+ ApiReply(p, m, NULL);
+ }
+
+ RtlFreeHeap(PsxHeap, 0,m);
+}
+
+BOOLEAN
+LocalPipeWrite (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements write when the device being written
+ is a local pipe.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the request.
+
+ Fd - supplies the address of the file descriptor being written.
+
+Return Value:
+
+ TRUE - the routine completed, and a reply should be sent
+ FALSE - the routine was blocked, no reply should be sent
+
+--*/
+
+{
+ PPSX_WRITE_MSG args;
+ PLOCAL_PIPE Pipe;
+ LONG Chunk, RoomInPipe;
+ LONG cb;
+ PUCHAR WriteDataPoint, ProcessBuffer;
+ NTSTATUS st;
+ PINTCB IntCb;
+ PPSX_PROCESS WaitingReader;
+ LARGE_INTEGER Time;
+ ULONG PosixTime;
+ NTSTATUS Status;
+
+ args = &m->u.Write;
+
+ Pipe = (PLOCAL_PIPE) Fd->SystemOpenFileDesc->IoNode->Context;
+
+ RtlEnterCriticalSection(&Pipe->CriticalSection);
+
+ //
+ // If we're writing to a pipe with no readers connected, we return
+ // EPIPE and send a SIGPIPE to the process. Broken pipe, call a
+ // plumber.
+ //
+
+ if (0 == Pipe->ReadHandleCount) {
+ RtlLeaveCriticalSection(&Pipe->CriticalSection);
+ m->Error = EPIPE;
+ AcquireProcessStructureLock();
+ PsxSignalProcess(p, SIGPIPE);
+ ReleaseProcessStructureLock();
+ return TRUE;
+ }
+
+ //
+ // if requested write size is greater than buffer size,
+ // write must be broken up into Pipe->BufferSize atomic
+ // chunks. If this is the case, Scratch1 is used to record
+ // amount of data transfered so far, and Scratch2 is used to
+ // record the number of bytes left in the total transfer
+ //
+
+ if (args->Nbytes > Pipe->BufferSize) {
+ args->Scratch2 = args->Nbytes - Pipe->BufferSize;
+ args->Nbytes = Pipe->BufferSize;
+ }
+
+ RoomInPipe = Pipe->BufferSize - Pipe->DataInPipe;
+
+ if (args->Nbytes > RoomInPipe) {
+
+ //
+ // There is not enough space in the pipe for the write to
+ // succeed. If the O_NONBLOCK flag is set, write whatever
+ // will fit. Otherwise, block the write and wait for a read
+ // to empty some of the data.
+ //
+
+ if (Fd->SystemOpenFileDesc->Flags & PSX_FD_NOBLOCK) {
+ args->Nbytes = 1;
+ if (args->Nbytes > RoomInPipe) {
+ m->Error = EAGAIN;
+ return TRUE;
+ }
+ // continue below to write
+ } else {
+
+ Status = BlockProcess(p, (PVOID)p, LocalPipeWriteHandler, m,
+ &Pipe->WaitingWriters, &Pipe->CriticalSection);
+ if (!NT_SUCCESS(Status)) {
+ m->Error = PsxStatusToErrno(Status);
+ return TRUE;
+ }
+
+ //
+ // Successfully blocked -- don't reply to message.
+ //
+ return FALSE;
+ }
+ }
+
+ //
+ // there is room in the pipe for the write to occur
+ //
+
+ WriteDataPoint = Pipe->WritePointer;
+ ProcessBuffer = (PUCHAR) args->Buf;
+
+ if ((ULONG)WriteDataPoint + args->Nbytes >
+ (ULONG)&Pipe->Buffer[Pipe->BufferSize-1]) {
+
+ Chunk = (ULONG) &Pipe->Buffer[Pipe->BufferSize-1] -
+ (ULONG)WriteDataPoint + 1;
+
+ } else {
+
+ Chunk = args->Nbytes;
+ }
+
+ st = NtReadVirtualMemory(p->Process, ProcessBuffer, WriteDataPoint,
+ (ULONG)Chunk, (PULONG)&cb);
+
+ if (!NT_SUCCESS(st) || cb != Chunk) {
+
+ //
+ // If the read did not work, then report as IO error
+ //
+
+ RtlLeaveCriticalSection(&Pipe->CriticalSection);
+ m->Error = EIO;
+ return TRUE;
+ }
+
+ ProcessBuffer += Chunk;
+
+ if (Chunk < args->Nbytes) {
+ Chunk = args->Nbytes - Chunk;
+
+ WriteDataPoint = &Pipe->Buffer[0];
+
+ st = NtReadVirtualMemory(p->Process, ProcessBuffer, WriteDataPoint,
+ (ULONG)Chunk, (PULONG)&cb);
+
+ if (!NT_SUCCESS(st) || cb != Chunk) {
+
+ //
+ // If the read did not work, then report as IO error
+ //
+
+ RtlLeaveCriticalSection(&Pipe->CriticalSection);
+ m->Error = EIO;
+ return TRUE;
+ }
+ Pipe->WritePointer = (PUCHAR)((ULONG)WriteDataPoint + Chunk);
+ } else {
+ Pipe->WritePointer = (PUCHAR)((ULONG)WriteDataPoint + Chunk);
+ }
+
+ if (Pipe->WritePointer > &Pipe->Buffer[Pipe->BufferSize - 1]) {
+ Pipe->WritePointer = &Pipe->Buffer[0];
+ }
+
+ Pipe->DataInPipe += args->Nbytes;
+
+ if (Pipe->DataInPipe > Pipe->BufferSize) {
+ Panic("LocalPipeWrite: Oops\n");
+ }
+
+ // Update ctime and mtime in IoNode - done in subsystem for local pipes
+
+ NtQuerySystemTime(&Time);
+ if (!RtlTimeToSecondsSince1970(&Time, &PosixTime)) {
+ PosixTime = 0L; // Time not within range of 1970 - 2105
+ }
+
+ RtlEnterCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
+ Fd->SystemOpenFileDesc->IoNode->ModifyDataTime =
+ Fd->SystemOpenFileDesc->IoNode->ModifyIoNodeTime = PosixTime;
+ RtlLeaveCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
+
+ m->ReturnValue += args->Nbytes;
+ args->Buf += args->Nbytes;
+
+ //
+ // Check for WaitingReaders. If any are found, then kick em
+ //
+
+ RtlEnterCriticalSection(&BlockLock);
+
+ if (!IsListEmpty(&Pipe->WaitingReaders)) {
+ IntCb = (PINTCB)Pipe->WaitingReaders.Flink;
+ IntCb = CONTAINING_RECORD(IntCb,INTCB,Links);
+ WaitingReader = (PPSX_PROCESS) IntCb->IntContext;
+
+ RtlLeaveCriticalSection(&Pipe->CriticalSection);
+
+ UnblockProcess(WaitingReader, IoCompletionInterrupt, TRUE, 0);
+
+ //
+ // Determine if this is a broken up long write. If Scratch2 is
+ // non-zero then more transfers need to occur. If Scratch1 is
+ // non-zero, then update to account for data transfered in this
+ // iteration.
+ //
+
+ } else {
+
+ RtlLeaveCriticalSection(&BlockLock);
+
+ RtlLeaveCriticalSection(&Pipe->CriticalSection);
+ }
+
+ //
+ // If we're doing non-blocking io, we've written what will fit into
+ // the pipe and we should return to the user now.
+ //
+
+ if (Fd->SystemOpenFileDesc->Flags & PSX_FD_NOBLOCK) {
+
+ return TRUE;
+ }
+
+ //
+ // Determine if this is a broken up long write. If Scratch2 is
+ // non-zero then more transfers need to occur. If Scratch1 is
+ // non-zero, then update to account for data transfered in this
+ // iteration.
+ //
+
+ if (args->Scratch2) {
+ args->Nbytes = args->Scratch2;
+ args->Scratch2 = 0;
+
+ return LocalPipeWrite(p, m, Fd);
+ }
+
+ return TRUE;
+}
+
+VOID
+LocalPipeReadHandler(
+ IN PPSX_PROCESS p,
+ IN PINTCB IntControlBlock,
+ IN PSX_INTERRUPTREASON InterruptReason,
+ IN int Signal // signal causing wakeup, if any
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure is called when data appears in a pipe and the process
+ specified by p has placed itself on the WaitingReaders queue for the pipe.
+
+Arguments:
+
+ p - Supplies the address of the process being interrupted.
+
+ IntControlBlock - Supplies the address of the interrupt control block.
+
+ InterruptReason - Supplies the reason that this process is being
+ interrupted. Not used in this handler.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PFILEDESCRIPTOR Fd;
+ BOOLEAN reply;
+ PPSX_API_MSG m;
+ PPSX_READ_MSG args;
+
+ RtlLeaveCriticalSection(&BlockLock);
+
+ m = IntControlBlock->IntMessage;
+ args = &m->u.Read;
+
+ if (InterruptReason == SignalInterrupt) {
+ //
+ // The read was interrupted by a signal. Bail out of
+ // service and let the interrupt be handled
+ //
+
+ RtlFreeHeap(PsxHeap, 0, IntControlBlock);
+
+ m->Error = EINTR;
+ m->Signal = Signal;
+
+ ApiReply(p, m, NULL);
+ RtlFreeHeap(PsxHeap, 0, m);
+ return;
+ }
+
+ //
+ // IoCompletionInterrupt
+ //
+
+ Fd = FdIndexToFd(p, args->FileDes);
+
+ if (!Fd) {
+ Panic("LocalPipeReadHandler: FdIndex");
+ }
+
+ reply = LocalPipeRead(p, m, Fd);
+
+ RtlFreeHeap(PsxHeap, 0, IntControlBlock);
+
+ if (reply) {
+ ApiReply(p, m, NULL);
+ }
+
+ RtlFreeHeap(PsxHeap, 0, m);
+}
+
+BOOLEAN
+LocalPipeRead (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements read when the device being read
+ is a local pipe.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the request.
+
+ Fd - supplies the address of the file descriptor being read.
+
+Return Value:
+
+ TRUE if the read completed, FALSE if the process should block.
+
+--*/
+
+{
+ PPSX_READ_MSG args, WaitingArgs;
+ PPSX_PROCESS WaitingWriter;
+ PLOCAL_PIPE Pipe;
+ LONG Chunk, RoomInPipe, LargestRead;
+ LONG cb;
+ PUCHAR ReadDataPoint, ProcessBuffer;
+ NTSTATUS st;
+ PPSX_API_MSG WaitingM;
+ PLIST_ENTRY Next;
+ PINTCB IntCb;
+ LARGE_INTEGER Time;
+ ULONG PosixTime;
+
+ args = &m->u.Read;
+
+ //
+ // check to see if any process has the pipe open for write
+ //
+
+ Pipe = (PLOCAL_PIPE)Fd->SystemOpenFileDesc->IoNode->Context;
+ ASSERT(NULL != Pipe);
+
+ RtlEnterCriticalSection(&Pipe->CriticalSection);
+
+ if (0 == Pipe->WriteHandleCount && !Pipe->DataInPipe) {
+ //
+ // Reading from an empty pipe with no writers attached gets you
+ // 0 (EOF).
+ //
+ RtlLeaveCriticalSection(&Pipe->CriticalSection);
+ m->ReturnValue = 0;
+ return TRUE;
+ }
+
+ if (!Pipe->DataInPipe) {
+ //
+ // if we have the pipe open O_NOBLOCK, then simply
+ // return EAGAIN
+ //
+
+ if (Fd->SystemOpenFileDesc->Flags & PSX_FD_NOBLOCK) {
+ RtlLeaveCriticalSection(&Pipe->CriticalSection);
+ m->Error = EAGAIN;
+ return TRUE;
+ }
+
+ //
+ // There is no data in the pipe. Set up an interrupt control
+ // block to wait for data and then block.
+ //
+
+ st = BlockProcess(p, (PVOID)p, LocalPipeReadHandler, m,
+ &Pipe->WaitingReaders, &Pipe->CriticalSection);
+ if (!NT_SUCCESS(st)) {
+ m->Error = PsxStatusToErrno(st);
+ return TRUE;
+ }
+
+ //
+ // Successfully blocked -- don't reply to api request.
+ //
+ return FALSE;
+ }
+
+ //
+ // If there is any data in the pipe, then compute the largest
+ // read size. Then figure out if it has to be broken into two
+ // transfers in order to turn the circular buffer boundary.
+ //
+
+ if (args->Nbytes > Pipe->DataInPipe) {
+ LargestRead = Pipe->DataInPipe;
+ } else {
+ LargestRead = args->Nbytes;
+ }
+
+ ReadDataPoint = Pipe->ReadPointer;
+ ProcessBuffer = (PUCHAR)args->Buf;
+
+ //
+ // determine if read can be done in one piece, or if
+ // the read has to be done in two pieces.
+ //
+
+ if ((ULONG)ReadDataPoint + LargestRead >
+ (ULONG)&Pipe->Buffer[Pipe->BufferSize - 1]) {
+ Chunk = (ULONG)&Pipe->Buffer[Pipe->BufferSize - 1] -
+ (ULONG)ReadDataPoint + 1;
+ } else {
+ Chunk = LargestRead;
+ }
+
+ //
+ // transfer from the pipe to the reading process
+ //
+
+ st = NtWriteVirtualMemory(p->Process, ProcessBuffer, ReadDataPoint,
+ (ULONG)Chunk, (PULONG)&cb);
+ if (!NT_SUCCESS(st) || cb != Chunk ) {
+
+ //
+ // If the write did not work, then report as IO error
+ //
+
+ RtlLeaveCriticalSection(&Pipe->CriticalSection);
+ m->Error = EIO;
+ return TRUE;
+ }
+
+ ProcessBuffer += Chunk;
+
+ if (Chunk < LargestRead) {
+
+ //
+ // the read wraps the pipe boundry. Transfer the second part of
+ // the read.
+ //
+
+ Chunk = LargestRead - Chunk;
+ ReadDataPoint = &Pipe->Buffer[0];
+
+ st = NtWriteVirtualMemory(p->Process, ProcessBuffer,
+ ReadDataPoint, (ULONG)Chunk, (PULONG)&cb);
+
+ if (!NT_SUCCESS(st) || cb != Chunk) {
+
+ //
+ // If the read did not work, then report as IO error
+ //
+
+ RtlLeaveCriticalSection(&Pipe->CriticalSection);
+ m->Error = EIO;
+ return TRUE;
+ }
+
+ Pipe->ReadPointer = (PUCHAR)((ULONG)ReadDataPoint + Chunk);
+ } else {
+ Pipe->ReadPointer = (PUCHAR)((ULONG)ReadDataPoint + Chunk);
+ }
+
+ if (Pipe->ReadPointer > &Pipe->Buffer[Pipe->BufferSize - 1]) {
+ Pipe->ReadPointer = &Pipe->Buffer[0];
+ }
+
+ //
+ // Adjust DataInPipe to account for read. Then check if there
+ // are any writers present. Pick a writer and kick him
+ //
+
+ Pipe->DataInPipe -= LargestRead;
+
+ m->ReturnValue = LargestRead;
+
+ RoomInPipe = Pipe->BufferSize - Pipe->DataInPipe;
+
+ // Update atime in IoNode - done in subsystem for local pipes
+
+ NtQuerySystemTime(&Time);
+ if ( !RtlTimeToSecondsSince1970(&Time, &PosixTime) ) {
+ PosixTime = 0L; // Time not within range of 1970 - 2105
+ }
+
+ RtlEnterCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
+ Fd->SystemOpenFileDesc->IoNode->AccessDataTime = PosixTime;
+ RtlLeaveCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
+
+ //
+ // Check for WaitingWriters. If any are found, then kick em
+ //
+
+ RtlEnterCriticalSection(&BlockLock);
+
+ if (!IsListEmpty(&Pipe->WaitingWriters)) {
+
+ //
+ // If there are waiting writers, then pick a writer
+ // and unblock him. The first writer whose current
+ // write count that is less than or equal to the room
+ // in pipe is chosen.
+ //
+
+ Next = Pipe->WaitingWriters.Flink;
+
+ while (Next != &Pipe->WaitingWriters) {
+ IntCb = CONTAINING_RECORD(Next, INTCB, Links);
+
+ WaitingM = IntCb->IntMessage;
+ WaitingArgs = &WaitingM->u.Read;
+ WaitingWriter = (PPSX_PROCESS) IntCb->IntContext;
+
+ if (WaitingArgs->Nbytes <= RoomInPipe) {
+ RtlLeaveCriticalSection(&Pipe->CriticalSection);
+ UnblockProcess(WaitingWriter,
+ IoCompletionInterrupt, TRUE, 0);
+ return TRUE;
+ }
+
+ Next = Next->Flink;
+ }
+ }
+
+ RtlLeaveCriticalSection(&BlockLock);
+ RtlLeaveCriticalSection(&Pipe->CriticalSection);
+
+ return TRUE;
+}
+
+
+BOOLEAN
+LocalPipeDup(
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd,
+ IN PFILEDESCRIPTOR FdDup
+ )
+{
+ PPSX_DUP_MSG args;
+ PLOCAL_PIPE Pipe;
+
+ args = &m->u.Dup;
+
+ //
+ // Copy contents of source file descriptor
+ // Note that FD_CLOEXEC must be CLEAR in FdDup.
+ //
+ *FdDup = *Fd;
+ FdDup->Flags &= ~PSX_FD_CLOSE_ON_EXEC;
+
+ //
+ // Increment reference count assocated with the SystemOpenFile
+ // descriptor for this file.
+ //
+
+ RtlEnterCriticalSection(&SystemOpenFileLock);
+ Fd->SystemOpenFileDesc->HandleCount++;
+ RtlLeaveCriticalSection(&SystemOpenFileLock);
+
+ Pipe = (PLOCAL_PIPE)Fd->SystemOpenFileDesc->IoNode->Context;
+
+ RtlEnterCriticalSection(&Pipe->CriticalSection);
+ if (Fd->SystemOpenFileDesc->Flags & PSX_FD_READ) {
+ ++Pipe->ReadHandleCount;
+ }
+ if (Fd->SystemOpenFileDesc->Flags & PSX_FD_WRITE) {
+ ++Pipe->WriteHandleCount;
+ }
+ RtlLeaveCriticalSection(&Pipe->CriticalSection);
+
+ return TRUE;
+}
+
+
+//
+// Named Pipes are very similar to local pipes
+// once the opens have completed. For that reason,
+// they share read/write io routines.
+//
+
+VOID
+NamedPipeOpenHandler(
+ IN PPSX_PROCESS p,
+ IN PINTCB IntControlBlock,
+ IN PSX_INTERRUPTREASON InterruptReason,
+ IN int Signal // Signal causing interruption, if any
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure is called when a process waiting for a named pipe
+ open to complete is either interrupted, or the pipe is opened.
+
+Arguments:
+
+ p - Supplies the address of the process being interrupted.
+
+ IntControlBlock - Supplies the address of the interrupt control block.
+
+ InterruptReason - Supplies the reason that this process is being
+ interrupted. Not used in this handler.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PFILEDESCRIPTOR Fd;
+ PPSX_API_MSG m;
+ PPSX_OPEN_MSG args;
+ PLOCAL_PIPE Pipe;
+ PLIST_ENTRY ListToScan;
+ PPSX_PROCESS Waiter;
+ PPSX_API_MSG WaitingM;
+ PLIST_ENTRY Next;
+ PINTCB IntCb;
+
+ RtlLeaveCriticalSection(&BlockLock);
+
+ m = IntControlBlock->IntMessage;
+ args = &m->u.Open;
+
+ RtlFreeHeap(PsxHeap, 0, IntControlBlock);
+
+ if (InterruptReason == SignalInterrupt) {
+
+ //
+ // The open was interrupted by a signal. Bail out of
+ // service by closing the half opened pipe and let
+ // the interrupt be handled
+ //
+
+ m->Error = EINTR;
+ m->Signal = Signal;
+
+ DeallocateFd(p, m->ReturnValue);
+
+ ApiReply(p, m, NULL);
+ RtlFreeHeap(PsxHeap, 0,m);
+ return;
+ }
+
+ //
+ // This Open Should be completed.
+ // Determine the list to scan to
+ // see if more opens should be completed
+ // at this time.
+ //
+
+ Fd = FdIndexToFd(p, m->ReturnValue);
+ ASSERT(Fd);
+
+ Pipe = (PLOCAL_PIPE)Fd->SystemOpenFileDesc->IoNode->Context;
+
+ //
+ // The list to scan is the list this process just came off
+ //
+
+ if (Fd->SystemOpenFileDesc->Flags & PSX_FD_READ) {
+ ListToScan = &Pipe->WaitingReaders;
+ } else {
+ ListToScan = &Pipe->WaitingWriters;
+ }
+
+ RtlEnterCriticalSection(&Pipe->CriticalSection);
+
+ RtlEnterCriticalSection(&BlockLock);
+
+ if (!IsListEmpty(ListToScan)) {
+
+ //
+ // Scan list to see if there are processes waiting in an
+ // open whose wait can be satisfied.
+ //
+
+ Next = ListToScan->Flink;
+
+ while ( Next != ListToScan ) {
+ IntCb = CONTAINING_RECORD(Next,INTCB,Links);
+
+ WaitingM = IntCb->IntMessage;
+
+ if ( WaitingM->ApiNumber == PsxOpenApi ) {
+
+ Waiter = (PPSX_PROCESS) IntCb->IntContext;
+
+ RtlLeaveCriticalSection(&Pipe->CriticalSection);
+
+ UnblockProcess(Waiter, IoCompletionInterrupt, TRUE, 0);
+ ApiReply(p, m, NULL);
+ RtlFreeHeap(PsxHeap, 0, m);
+ return;
+ }
+
+ Next = Next->Flink;
+ }
+ }
+
+ RtlLeaveCriticalSection(&BlockLock);
+
+ RtlLeaveCriticalSection(&Pipe->CriticalSection);
+
+ ApiReply(p, m, NULL);
+ RtlFreeHeap(PsxHeap, 0, m);
+}
+
+BOOLEAN
+NamedPipeOpenNewHandle (
+ IN PPSX_PROCESS p,
+ IN PFILEDESCRIPTOR Fd,
+ IN OUT PPSX_API_MSG m
+ )
+{
+ NTSTATUS Status;
+ PLOCAL_PIPE Pipe;
+ PULONG CountToTest;
+ PLIST_ENTRY ListToBlockOn;
+ PLIST_ENTRY ListToScan;
+ PPSX_PROCESS Waiter;
+ PPSX_API_MSG WaitingM;
+ PLIST_ENTRY Next;
+ PINTCB IntCb;
+
+ Pipe = (PLOCAL_PIPE)Fd->SystemOpenFileDesc->IoNode->Context;
+
+ LocalPipeNewHandle(p, Fd);
+
+ if ((Fd->SystemOpenFileDesc->Flags & (PSX_FD_READ | PSX_FD_WRITE)) ==
+ (PSX_FD_READ | PSX_FD_WRITE)) {
+ return TRUE;
+ }
+
+ if (Fd->SystemOpenFileDesc->Flags & PSX_FD_NOBLOCK) {
+ if (Fd->SystemOpenFileDesc->Flags & PSX_FD_READ) {
+ return TRUE;
+ } else {
+
+ RtlEnterCriticalSection(&Pipe->CriticalSection);
+
+ if (!Pipe->ReadHandleCount) {
+ m->Error = ENXIO;
+ RtlLeaveCriticalSection(&Pipe->CriticalSection);
+ DeallocateFd(p,m->ReturnValue);
+ } else {
+ RtlLeaveCriticalSection(&Pipe->CriticalSection);
+ }
+ return TRUE;
+ }
+ } else {
+
+ //
+ // Pipe is not being opened O_NONBLOCK. If pipe is being opened
+ // for read, then wait for a writer; otherwise, wait for
+ // a reader
+ //
+
+ if (Fd->SystemOpenFileDesc->Flags & PSX_FD_READ) {
+ CountToTest = &Pipe->WriteHandleCount;
+ ListToBlockOn = &Pipe->WaitingReaders;
+ ListToScan = &Pipe->WaitingWriters;
+ } else {
+ CountToTest = &Pipe->ReadHandleCount;
+ ListToBlockOn = &Pipe->WaitingWriters;
+ ListToScan = &Pipe->WaitingReaders;
+ }
+
+ RtlEnterCriticalSection(&Pipe->CriticalSection);
+
+ if (!*CountToTest) {
+
+ Status = BlockProcess(p, (PVOID)p, NamedPipeOpenHandler, m,
+ ListToBlockOn, &Pipe->CriticalSection);
+ if (!NT_SUCCESS(Status)) {
+ m->Error = PsxStatusToErrno(Status);
+ return TRUE;
+ }
+
+ //
+ // The process is successfully blocked -- do not reply to the api
+ // request.
+ //
+ return FALSE;
+
+ } else {
+ RtlEnterCriticalSection(&BlockLock);
+ if (!IsListEmpty(ListToScan)) {
+
+ //
+ // Scan list to see if there are processes waiting in an
+ // open whose wait can be satisfied.
+ //
+
+ Next = ListToScan->Flink;
+
+ while (Next != ListToScan) {
+ IntCb = CONTAINING_RECORD(Next,INTCB,Links);
+
+ WaitingM = IntCb->IntMessage;
+
+ if (WaitingM->ApiNumber == PsxOpenApi) {
+ Waiter = (PPSX_PROCESS) IntCb->IntContext;
+ RtlLeaveCriticalSection(&Pipe->CriticalSection);
+ UnblockProcess(Waiter, IoCompletionInterrupt, TRUE, 0);
+ return TRUE;
+ }
+ Next = Next->Flink;
+ }
+ }
+
+ RtlLeaveCriticalSection(&BlockLock);
+
+ }
+
+ RtlLeaveCriticalSection(&Pipe->CriticalSection);
+ return TRUE;
+ }
+}
+
+VOID
+NamedPipeLastClose (
+ IN PPSX_PROCESS p,
+ IN PSYSTEMOPENFILE SystemOpenFile
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called when the last handle to a local
+ pipe is closed. Its function is to tear down the pipe.
+
+Arguments:
+
+ SystemOpenFile - Supplies the system open file describing the
+ pipe being closed.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ NTSTATUS st;
+
+ st = NtClose(SystemOpenFile->NtIoHandle);
+ ASSERT(NT_SUCCESS(st));
+}
+
+
+PSXIO_VECTORS NamedPipeVectors = {
+ NamedPipeOpenNewHandle,
+ LocalPipeNewHandle,
+ LocalPipeClose,
+ NamedPipeLastClose,
+ LocalPipeIoNodeClose,
+ LocalPipeRead,
+ LocalPipeWrite,
+ LocalPipeDup,
+ LocalPipeLseek,
+ LocalPipeStat
+ };
diff --git a/private/posix/psxss/makefile b/private/posix/psxss/makefile
new file mode 100644
index 000000000..afc6030de
--- /dev/null
+++ b/private/posix/psxss/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT.
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/posix/psxss/nullio.c b/private/posix/psxss/nullio.c
new file mode 100644
index 000000000..022306f8d
--- /dev/null
+++ b/private/posix/psxss/nullio.c
@@ -0,0 +1,421 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ nullio.c
+
+Abstract:
+
+ This module implements io on the 'null' device. It's pretty
+ simple.
+
+Author:
+
+ Matthew Bradburn (mattbr) 01-Aug-1995
+
+Revision History:
+
+--*/
+
+
+#include <sys/stat.h>
+#include <time.h>
+#include <wchar.h>
+#include "psxsrv.h"
+
+BOOLEAN
+NullOpen(
+ IN PPSX_PROCESS p,
+ IN PFILEDESCRIPTOR Fd,
+ IN OUT PPSX_API_MSG m
+ );
+
+BOOLEAN
+NullRead(
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd
+ );
+
+BOOLEAN
+NullWrite(
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd
+ );
+
+BOOLEAN
+NullDup(
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd,
+ IN PFILEDESCRIPTOR FdDup
+ );
+
+BOOLEAN
+NullLseek(
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd
+ );
+
+BOOLEAN
+NullStat(
+ IN PIONODE IoNode,
+ IN HANDLE FileHandle,
+ OUT struct stat *StatBuf,
+ OUT NTSTATUS *pStatus
+ );
+
+void
+FindOwnerModeFile(
+ IN HANDLE FileHandle,
+ OUT struct stat *StatBuf
+ );
+
+PSXIO_VECTORS NullVectors = {
+ NullOpen, // OpenNewHandle
+ NULL, // NewHandle
+ NULL, // Close
+ NULL, // LastClose
+ NULL, // IoNodeClose
+ NullRead, // Read
+ NullWrite, // Write
+ NullDup, // Dup
+ NullLseek, // Lseek
+ NullStat // Stat
+ };
+
+
+BOOLEAN
+NullOpen(
+ IN PPSX_PROCESS p,
+ IN PFILEDESCRIPTOR Fd,
+ IN OUT PPSX_API_MSG m
+ )
+/*++
+
+Routine Description:
+
+ This routine
+
+Arguments:
+
+
+
+Return Value:
+
+ FALSE - Failure.
+ TRUE - Success.
+
+--*/
+{
+ PPSX_OPEN_MSG args;
+ LARGE_INTEGER time;
+ ULONG posix_time;
+
+ args = &m->u.Open;
+
+ NtQuerySystemTime(&time);
+ if (!RtlTimeToSecondsSince1970(&time, &posix_time)) {
+ posix_time = 0;
+ }
+
+KdPrint(("Posix time: %x\n", posix_time));
+
+ RtlEnterCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
+ Fd->SystemOpenFileDesc->IoNode->ModifyDataTime = posix_time;
+ Fd->SystemOpenFileDesc->IoNode->ModifyIoNodeTime = posix_time;
+ Fd->SystemOpenFileDesc->IoNode->AccessDataTime = posix_time;
+ RtlLeaveCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
+
+ return TRUE;
+}
+
+BOOLEAN
+NullWrite(
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements write when the device being written
+ is the null device. Writes to the null device succeed, and the
+ data is discarded.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the request.
+
+ Fd - supplies the address of the file descriptor being written.
+
+Return Value:
+
+--*/
+
+{
+ PPSX_WRITE_MSG args;
+ LARGE_INTEGER time;
+ ULONG posix_time;
+ NTSTATUS st;
+
+ args = &m->u.Write;
+
+ if (args->Nbytes > 0) {
+
+ //
+ // Update the times for stat.
+ //
+
+ NtQuerySystemTime(&time);
+ if (!RtlTimeToSecondsSince1970(&time, &posix_time)) {
+ posix_time = 0;
+ }
+
+ RtlEnterCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
+ Fd->SystemOpenFileDesc->IoNode->ModifyDataTime = posix_time;
+ Fd->SystemOpenFileDesc->IoNode->ModifyIoNodeTime = posix_time;
+ RtlLeaveCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
+ }
+
+ m->ReturnValue = args->Nbytes;
+ return TRUE;
+}
+
+
+BOOLEAN
+NullRead(
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements read when the device being read
+ is the null device. Reads from this device always return EOF.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the request.
+
+ Fd - supplies the address of the file descriptor being read.
+
+Return Value:
+
+--*/
+
+{
+ PPSX_READ_MSG args;
+ NTSTATUS st;
+ LARGE_INTEGER ByteOffset;
+ ULONG IoBufferSize;
+ LARGE_INTEGER Time;
+ ULONG posix_time;
+
+ args = &m->u.Read;
+
+ if (args->Nbytes > 0) {
+
+ //
+ // Update the access time on the ionode.
+ //
+
+ NtQuerySystemTime(&Time);
+ if (!RtlTimeToSecondsSince1970(&Time, &posix_time)) {
+ posix_time = 0;
+ }
+
+ RtlEnterCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
+ Fd->SystemOpenFileDesc->IoNode->AccessDataTime = posix_time;
+ RtlLeaveCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
+ }
+
+ m->ReturnValue = 0;
+ return TRUE;
+}
+
+
+BOOLEAN
+NullDup(
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd,
+ IN PFILEDESCRIPTOR FdDup
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements dup and dup2.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the request.
+
+ Fd - supplies the address of the file descriptor being duplicated.
+
+ FdDup - supplies the address of the duplicate file descriptor.
+
+Return Value:
+
+ TRUE.
+--*/
+
+{
+ PPSX_DUP_MSG args;
+
+ args = &m->u.Dup;
+
+ //
+ // Copy contents of source file descriptor slot into new descriptor
+ // Note that FD_CLOEXEC must be CLEAR on FdDup.
+ //
+
+ *FdDup = *Fd;
+ FdDup->Flags &= ~PSX_FD_CLOSE_ON_EXEC;
+
+ //
+ // Increment reference count associated with the SystemOpenFile
+ // descriptor for this file.
+ //
+
+ // Grab system open file lock
+
+ RtlEnterCriticalSection(&SystemOpenFileLock);
+
+ Fd->SystemOpenFileDesc->HandleCount++;
+
+ RtlLeaveCriticalSection(&SystemOpenFileLock);
+
+ return TRUE;
+}
+
+
+BOOLEAN
+NullLseek(
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m,
+ IN PFILEDESCRIPTOR Fd
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements lseek when the device being seeked on
+ is the null device. We allow these seeks, but they have no effect.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the request.
+
+ Fd - supplies the address of the file descriptor being seekd
+
+Return Value:
+
+ TRUE
+
+
+--*/
+
+{
+ PPSX_LSEEK_MSG args;
+ NTSTATUS st;
+ LARGE_INTEGER Offset, NewByteOffset;
+
+ args = &m->u.Lseek;
+
+ Offset = RtlConvertLongToLargeInteger(args->Offset);
+
+ NewByteOffset = Offset;
+
+ if (SEEK_CUR != args->Whence && SEEK_SET != args->Whence &&
+ SEEK_END != args->Whence) {
+
+ m->Error = EINVAL;
+ return TRUE;
+ }
+
+ // Check for overflow. POSIX limited to arithmetic data type for off_t
+
+ if (NewByteOffset.HighPart != 0 || (off_t)NewByteOffset.LowPart < 0) {
+ m->Error = EINVAL;
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+NullStat(
+ IN PIONODE IoNode,
+ IN HANDLE FileHandle,
+ OUT struct stat *StatBuf,
+ OUT NTSTATUS *pStatus
+ )
+/*++
+
+Routine Description:
+
+ This procedure implements stat when the device being read
+ is the null device.
+
+Arguments:
+
+ IoNode - supplies a pointer to the ionode of the file for which stat is
+ requested. NULL if no active Ionode entry.
+
+ FileHandle - supplies the Nt file handle of the file .
+
+ StatBuf - Supplies the address of the statbuf portion of the message
+ associated with the request.
+
+Return Value:
+
+ TRUE.
+
+--*/
+{
+ IO_STATUS_BLOCK Iosb;
+
+ //
+ // First get the static information on the file from the ionode if
+ // there is one (i.e. if the file currently open.
+ // Open() sets the fields in the ionode.
+ //
+
+ if (NULL != IoNode) {
+ StatBuf->st_mode = IoNode->Mode;
+ StatBuf->st_ino = IoNode->FileSerialNumber;
+ StatBuf->st_dev = IoNode->DeviceSerialNumber;
+
+ StatBuf->st_atime = IoNode->AccessDataTime;
+ StatBuf->st_ctime = IoNode->ModifyIoNodeTime;
+ StatBuf->st_mtime = IoNode->ModifyDataTime;
+ }
+
+ StatBuf->st_uid = 0;
+ StatBuf->st_gid = 0;
+ StatBuf->st_size = 0;
+ StatBuf->st_nlink = 1;
+
+ return TRUE;
+}
diff --git a/private/posix/psxss/procblk.c b/private/posix/psxss/procblk.c
new file mode 100644
index 000000000..3ca06e796
--- /dev/null
+++ b/private/posix/psxss/procblk.c
@@ -0,0 +1,125 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ procblk.c
+
+Abstract:
+
+ This module implements process block and unblock
+
+Author:
+
+ Mark Lucovsky (markl) 30-Mar-1989
+
+Revision History:
+
+--*/
+
+
+#include "psxsrv.h"
+
+
+NTSTATUS
+BlockProcess(
+ IN PPSX_PROCESS p,
+ IN PVOID Context,
+ IN INTHANDLER Handler,
+ IN PPSX_API_MSG m,
+ IN PLIST_ENTRY BlockList OPTIONAL,
+ IN PRTL_CRITICAL_SECTION CriticalSectionToRelease OPTIONAL
+ )
+{
+ PINTCB IntCb;
+ PPSX_API_MSG NewM;
+ int Sig;
+
+ IntCb = RtlAllocateHeap(PsxHeap, 0, sizeof(INTCB));
+ if (NULL == IntCb) {
+ return STATUS_NO_MEMORY;
+ }
+
+ NewM = RtlAllocateHeap(PsxHeap, 0, sizeof(PSX_API_MSG));
+ if (NULL == NewM) {
+ RtlFreeHeap(PsxHeap, 0, IntCb);
+ return STATUS_NO_MEMORY;
+ }
+
+ *NewM = *m;
+
+ IntCb->IntHandler = Handler;
+ IntCb->IntMessage = NewM;
+ IntCb->IntContext = Context;
+
+ RtlEnterCriticalSection(&BlockLock);
+
+ p->IntControlBlock = IntCb;
+
+ if (ARGUMENT_PRESENT(BlockList)) {
+ InsertTailList(BlockList, &IntCb->Links);
+ } else {
+ InsertTailList(&DefaultBlockList, &IntCb->Links);
+ }
+
+ AcquireProcessLock(p);
+
+ //
+ // Check for signals
+ //
+
+ if (0 != (Sig = PsxCheckPendingSignals(p))) {
+
+ ReleaseProcessLock(p);
+
+ //
+ // Block is interrupted by a signal
+ //
+
+ if (ARGUMENT_PRESENT(CriticalSectionToRelease)) {
+ RtlLeaveCriticalSection(CriticalSectionToRelease);
+ }
+ UnblockProcess(p, SignalInterrupt, TRUE, Sig);
+
+ return STATUS_SUCCESS;
+
+ }
+ if (ARGUMENT_PRESENT(CriticalSectionToRelease)) {
+ RtlLeaveCriticalSection(CriticalSectionToRelease);
+ }
+
+ ReleaseProcessLock(p);
+ RtlLeaveCriticalSection(&BlockLock);
+
+ return STATUS_SUCCESS;
+}
+
+
+BOOLEAN
+UnblockProcess(
+ IN PPSX_PROCESS p,
+ IN PSX_INTERRUPTREASON InterruptReason,
+ IN BOOLEAN BlockLockHeld,
+ IN int Signal // Signal causing wakeup, if any
+ )
+{
+ PINTCB IntCb;
+ if (!BlockLockHeld) {
+ RtlEnterCriticalSection(&BlockLock);
+ }
+
+ if (p->IntControlBlock) {
+ IntCb = p->IntControlBlock;
+ RemoveEntryList(&IntCb->Links);
+ p->IntControlBlock = (PINTCB) NULL;
+ (IntCb->IntHandler)(p,IntCb,InterruptReason,Signal);
+ return TRUE;
+ }
+
+ if (!BlockLockHeld) {
+ RtlLeaveCriticalSection(&BlockLock);
+ }
+
+ return FALSE;
+}
diff --git a/private/posix/psxss/process.c b/private/posix/psxss/process.c
new file mode 100644
index 000000000..1fc02609e
--- /dev/null
+++ b/private/posix/psxss/process.c
@@ -0,0 +1,1914 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ process.c
+
+Abstract:
+
+ Implementation of PSX Process Structure Backbone.
+
+Author:
+
+ Mark Lucovsky (markl) 08-Mar-1989
+
+Revision History:
+
+--*/
+
+#include "psxsrv.h"
+#include "seposix.h"
+
+NTSTATUS
+PsxInitializeProcessStructure()
+
+/*++
+
+Routine Description:
+
+ This function initializes the PSX process structure. This includes
+creating an initial process table, open file tables, CLIENT_ID to PID hash
+table, and PID allocator.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status.
+
+--*/
+
+{
+ LONG i;
+ PPSX_PROCESS Process;
+ NTSTATUS Status;
+
+ //
+ // Initialize PsxProcessStructureLock
+ //
+
+ Status = RtlInitializeCriticalSection(&BlockLock);
+ ASSERT(NT_SUCCESS(Status));
+
+ InitializeListHead(&DefaultBlockList);
+
+ Status = RtlInitializeCriticalSection(&PsxProcessStructureLock);
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Initialize the ClientIdHashTable
+ //
+
+ for (i = 0; i < CIDHASHSIZE ; i++) {
+ InitializeListHead(&ClientIdHashTable[i]);
+ }
+
+ //
+ // Initialize Process Table
+ //
+
+ FirstProcess = PsxProcessTable;
+ LastProcess = &PsxProcessTable[32-1];
+
+ for (Process = FirstProcess; Process < LastProcess; Process++) {
+ Process->Flags |= P_FREE;
+ Process->SequenceNumber = 0;
+ }
+ return Status;
+
+}
+
+PPSX_PROCESS
+PsxAllocateProcess (
+ IN PCLIENT_ID ClientId
+ )
+
+/*++
+
+Routine Description:
+
+ This function allocates a slot in the process table for the process
+ with the specified CLIENT_ID. Once a free process table slot is
+ allocated, the process is placed in the ClientIdHashTable. The process
+ slot sequence number is incremented, and a Pid is assigned to the
+ process. The process' process lock in initialized, and the process
+ table lock is released. A pointer to the new process is returned.
+ The process lock of the new process remains held so that the caller
+ can further initialize the process.
+
+
+Arguments:
+
+ ClientId - Supplies the address of the client id associated with the
+ process. Only the UniqueProcess portion of the ClientId is used.
+
+Return Value:
+
+ Null - No free process table slot could be located.
+
+ Non-Null - A pointer to the allocated process. The process' process lock is
+ held, the process can be located in the ClientIdHashTable, and
+ the process has a Pid.
+
+--*/
+
+{
+ PPSX_PROCESS Process;
+ ULONG index;
+ NTSTATUS Status;
+
+ //
+ // Lock Process Table
+ //
+
+ AcquireProcessStructureLock();
+
+ for (Process = FirstProcess, index = 0; Process < LastProcess;
+ Process++, index++) {
+ if (!(Process->Flags & P_FREE)) {
+ continue;
+ }
+
+ //
+ // Slot Found. Mark process as allocated and free process table
+ // lock.
+ //
+
+ Process->Flags &= ~P_FREE;
+ Process->ClientId = *ClientId;
+ Process->ClientPort = NULL;
+
+ //
+ // Place Process In CLIENT_ID Hash Table
+ //
+
+ InsertTailList(&ClientIdHashTable[CIDTOHASHINDEX(ClientId)],
+ &Process->ClientIdHashLinks);
+
+ if (++Process->SequenceNumber > 255)
+ Process->SequenceNumber = 1;
+
+ MAKEPID(Process->Pid, Process->SequenceNumber, index);
+
+ if (Process->Pid == SPECIALPID) {
+ KdPrint(("PSXSS: seq %d, index %d\n", Process->SequenceNumber, index));
+ }
+ ASSERT(Process->Pid != SPECIALPID);
+
+ Status = RtlInitializeCriticalSection(&Process->ProcessLock);
+ if (!NT_SUCCESS(Status)) {
+ ReleaseProcessStructureLock();
+ return NULL;
+ }
+
+ AcquireProcessLock(Process);
+
+ //
+ // Unlock Process Table
+ //
+
+ ReleaseProcessStructureLock();
+
+ IF_PSX_DEBUG(EXEC) {
+ KdPrint(("PsxAllocateProcess: Process = %lx, ClientId %lx.%lx "
+ "Pid %lx\n", Process, Process->ClientId.UniqueProcess,
+ Process->ClientId.UniqueThread, Process->Pid));
+ }
+ return Process;
+ }
+
+ //
+ // Unlock Process Table
+ //
+
+ ReleaseProcessStructureLock();
+ return (PPSX_PROCESS)NULL;
+}
+
+
+PPSX_PROCESS
+PsxLocateProcessByClientId (
+ IN PCLIENT_ID ClientId
+ )
+
+/*++
+
+Routine Description:
+
+ This function attempts to locate the process with the specified CLIENT_ID.
+ It depends on the way PsxAllocateProcess allocates a process table
+ slot, assigns it a CLIENT_ID, and places it in the ClientIdHashTable while
+ holding the PsxProcessStructureLock. Only the UniqueProcess portion of the
+ specified CLIENT_ID is used.
+
+Arguments:
+
+ ClientId - Supplies the client id associated with the process to be located.
+
+Return Value:
+
+ Null - No process whose client id matches the specified ClientId parameter
+ is located in the ClientIdHashTable.
+
+ Non-Null - Returns the address of the process whose CLIENT_ID matches the
+ specified ClientId.
+
+--*/
+
+{
+ PPSX_PROCESS Process;
+ PLIST_ENTRY head, next;
+
+ head = &ClientIdHashTable[CIDTOHASHINDEX(ClientId)];
+
+ //
+ // Lock Process Table
+ //
+
+ AcquireProcessStructureLock();
+
+ next = head->Flink;
+ while (next != head) {
+ Process = CONTAINING_RECORD(next, PSX_PROCESS, ClientIdHashLinks);
+ if (Process->ClientId.UniqueProcess == ClientId->UniqueProcess &&
+ Process->ClientId.UniqueThread == ClientId->UniqueThread) {
+
+ //
+ // Unlock Process Table
+ //
+
+ ReleaseProcessStructureLock();
+ return Process;
+ }
+ next = next->Flink;
+ }
+
+ //
+ // Unlock Process Table
+ //
+
+ ReleaseProcessStructureLock();
+ return (PPSX_PROCESS)NULL;
+}
+
+PPSX_PROCESS
+PsxLocateProcessBySession (
+ IN PPSX_SESSION Session
+ )
+{
+ PPSX_PROCESS Process;
+
+ //
+ // Lock Process Table
+ //
+
+ AcquireProcessStructureLock();
+
+ for (Process = FirstProcess; Process < LastProcess; Process++) {
+ if (!(Process->Flags & P_FREE)) {
+ continue;
+ }
+
+ if (Process->PsxSession == Session) {
+
+ //
+ // Unlock Process Table
+ //
+
+ ReleaseProcessStructureLock();
+ return Process;
+ }
+ }
+
+ //
+ // Unlock Process Table
+ //
+
+ ReleaseProcessStructureLock();
+ return (PPSX_PROCESS)NULL;
+}
+
+BOOLEAN
+IsGroupOrphaned(
+ IN pid_t ProcessGroup
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called to determine if the specified ProcessGroup
+ is orphaned.
+
+ An orphaned process group is a group where the parent of every member
+ is itself a member, or is not a member of the group's session. This
+ definition is a little flawed since it assumes that processes without
+ a parent are inherited by a real process in a different session.
+
+
+Arguments:
+
+ ProcessGroup - Supplies the process group to check
+
+Return Value:
+
+ TRUE - The process group is orphaned.
+
+ FALSE - The process group is not orphaned.
+
+--*/
+
+{
+ PPSX_PROCESS Parent, FirstMember, NextMember;
+ PLIST_ENTRY Next;
+ BOOLEAN Orphaned,MemberFound;
+
+ LockNtSessionList();
+
+ //
+ // First locate a member of the group by scanning the process
+ // table. Once a group member is found, then whip through the
+ // group list looking to see if any member's parent is not in
+ // the session of the group, or whose parent is SPECIALPID
+ //
+
+ MemberFound = FALSE;
+
+ for (FirstMember = FirstProcess; FirstMember < LastProcess; FirstMember++) {
+ if (FirstMember->Flags & P_FREE) {
+ continue;
+ }
+
+ if (FirstMember->ProcessGroupId == ProcessGroup) {
+ MemberFound = TRUE;
+ break;
+ }
+ }
+
+ ASSERT(MemberFound);
+
+ Orphaned = FALSE;
+
+ //
+ // Scan the group. For each member, look at the members parent.
+ //
+ // If the parent is SPECIALPID, then the group is orphaned,
+ // else if the parent is not in the same group and is in a
+ // different session, then the group is oprphaned
+ //
+
+ NextMember = FirstMember;
+ do {
+
+ //
+ // The parent of a member is not in the same session so the
+ // group is orphaned.
+ //
+
+ if (NextMember->ParentPid == SPECIALPID) {
+ Orphaned = TRUE;
+ break;
+ } else {
+ //
+ // The member has a parent, so see if the parent is in
+ // a different group and in a different session. If this
+ // is the case, then the group is orphaned.
+ //
+
+ Parent = PIDTOPROCESS(NextMember->ParentPid);
+
+ if (Parent->ProcessGroupId != ProcessGroup &&
+ Parent->PsxSession != FirstMember->PsxSession) {
+ Orphaned = TRUE;
+ break;
+ }
+ }
+
+ Next = NextMember->GroupLinks.Flink;
+ NextMember = CONTAINING_RECORD(Next, PSX_PROCESS, GroupLinks);
+
+ } while (NextMember != FirstMember);
+
+ UnlockNtSessionList();
+
+ return Orphaned;
+}
+
+BOOLEAN
+PsxStopProcess(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m,
+ IN ULONG Signal,
+ IN sigset_t *RestoreBlockSigset OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called by PendingSignalHandledInside in response
+ to a pending stop signal. It simply blocks the process awaiting a
+ SIGCONT signal.
+
+Arguments:
+
+ p - Supplies the address of the process to stop
+
+ m - Supplies the reply message to be generated when the process is
+ continued
+
+ Signal - Supplies the signal number used to stop the process.
+ If the stop signal is anything other than SIGSTOP, a check is
+ made to see if the processes group is orphaned before stopping
+ the process.
+
+ RestoreBlockSigset - Contains an optional blocked signal mask to
+ be restured during the reply.
+
+ Locks - Process lock always released before return.
+
+Return Value:
+
+ TRUE - The process was stopped by this call.
+
+ FALSE - The process was not stopped.
+
+--*/
+
+{
+ sigset_t RestoreBlockMask;
+ PPSX_PROCESS Parent;
+ NTSTATUS Status;
+
+ if (ARGUMENT_PRESENT(RestoreBlockSigset)) {
+ RestoreBlockMask = *RestoreBlockSigset;
+ } else {
+ RestoreBlockMask = (sigset_t)0xffffffff;
+ }
+
+ ReleaseProcessLock(p);
+
+ AcquireProcessStructureLock();
+
+ if (Signal != SIGSTOP) {
+ if (IsGroupOrphaned(p->ProcessGroupId)) {
+ ReleaseProcessStructureLock();
+ return FALSE;
+ }
+ }
+
+
+ p->State = Stopped;
+ p->ExitStatus = (Signal << 8) | 0177;
+ p->Flags &= ~P_WAITED; // proc may satisfy wait
+
+ //
+ // Possibly generate a SIGCHLD to the parent, and satisfy
+ // a parent wait
+ //
+
+ if (p->ParentPid != SPECIALPID) {
+
+ Parent = PIDTOPROCESS(p->ParentPid);
+ AcquireProcessLock(Parent);
+
+ //
+ // if the SA_NOCLDSTOP flag is not sent, then SIGCHLD the parent
+ //
+
+ if (!Parent->SignalDataBase.SignalDisposition[SIGCHLD-1].sa_flags
+ & SA_NOCLDSTOP) {
+ PsxSignalProcess(Parent,SIGCHLD);
+ }
+
+ ReleaseProcessLock(Parent);
+
+ if (Parent->State == Waiting) {
+
+ Status = BlockProcess(p, (PVOID)RestoreBlockMask,
+ PsxStopProcessHandler, m, FALSE, &PsxProcessStructureLock);
+ if (!NT_SUCCESS(Status)) {
+ m->Error = PsxStatusToErrno(Status);
+ return FALSE;
+ }
+
+ UnblockProcess(Parent, WaitSatisfyInterrupt, FALSE, 0);
+ return TRUE;
+ }
+ }
+
+ Status = BlockProcess(p, (PVOID)RestoreBlockMask, PsxStopProcessHandler,
+ m, FALSE, &PsxProcessStructureLock);
+ if (!NT_SUCCESS(Status)) {
+ m->Error = PsxStatusToErrno(Status);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+VOID
+PsxStopProcessHandler(
+ IN PPSX_PROCESS p,
+ IN PINTCB IntControlBlock,
+ IN PSX_INTERRUPTREASON InterruptReason,
+ IN int Signal
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure is called when a stopped process is continued.
+
+Arguments:
+
+ p - Supplies the address of the process being continued.
+
+ IntControlBlock - Supplies the address of the interrupt control block.
+
+ InterruptReason - Supplies the reason that this process is being
+ interrupted. Not used in this handler.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PPSX_API_MSG m;
+ sigset_t RestoreBlockSigset;
+
+ UNREFERENCED_PARAMETER(InterruptReason);
+
+ RtlLeaveCriticalSection(&BlockLock);
+
+ RestoreBlockSigset = (sigset_t)IntControlBlock->IntContext;
+
+ m = IntControlBlock->IntMessage;
+ RtlFreeHeap(PsxHeap, 0, (PVOID)IntControlBlock);
+
+ m->Signal = Signal;
+
+ if ((ULONG)RestoreBlockSigset == 0xffffffff) {
+ ApiReply(p, m ,NULL);
+ } else {
+ ApiReply(p, m, &RestoreBlockSigset);
+ }
+ RtlFreeHeap(PsxHeap, 0, (PVOID)m);
+}
+
+VOID
+PsxSignalProcess(
+ IN PPSX_PROCESS p,
+ IN ULONG Signal
+ )
+
+/*++
+
+Routine Description:
+
+ This function causes the specified signal to be sent to the specified
+ process. Thsi works by simply marking the signal as pending, and then
+ making the process looking at ats pending signals.
+
+ This function must be called with the PsxProcessTable locked
+
+ Getting the process to look at its pending signals has three basic cases:
+
+ 1) The process is not in PSX.
+
+ Cause the process to call __NullPosixAPI
+
+ 2) The process is inside PSX and is not interruptible
+
+ Simply set signal to pending
+
+ 3) The process is inside PSX and is interruptible
+
+ Dispatch to the process' interrupt control block
+
+
+Arguments:
+
+ p - Supplies the address of the process to signal.
+
+ Signal - Supplies the Signal value.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ sigset_t Pending;
+
+ ASSERT(NULL != p);
+
+ AcquireProcessLock(p);
+
+ //
+ // This can happen due to exit recursion
+ //
+
+ if (p->State == Exited) {
+ ReleaseProcessLock(p);
+ return;
+ }
+
+ //
+ // if signal is SIGCONT, then clear any pending stop signals
+ // if signal is a stop signal, then clear any pending SIGCONT
+ //
+
+ switch (Signal) {
+ case SIGCONT:
+ p->SignalDataBase.PendingSignalMask &= ~_SIGSTOPSIGNALS;
+
+ if (p->State == Stopped) {
+ SIGADDSET(&p->SignalDataBase.PendingSignalMask,SIGCONT);
+ p->State = Active;
+ ReleaseProcessLock(p);
+ UnblockProcess(p, SignalInterrupt, FALSE, Signal);
+ return;
+ }
+ break;
+ case SIGSTOP:
+ case SIGTSTP:
+ case SIGTTIN:
+ case SIGTTOU:
+ //
+ // XXX.mjb: FIX to deal w/ orphaned group
+ //
+
+ p->SignalDataBase.PendingSignalMask &= ~(1L << (SIGCONT - 1));
+ break;
+ default:
+ break;
+ }
+
+ //
+ // If signal is being ignored, don't add it to the pending set.
+ //
+
+ if (p->SignalDataBase.SignalDisposition[Signal-1].sa_handler
+ == (_handler)SIG_IGN) {
+ ReleaseProcessLock(p);
+ return;
+ }
+
+ //
+ // Add signal to pending set. If any pending but not blocked signals
+ // exist for the process, then maybe make the process take a look at
+ // its pending signals.
+ //
+
+ SIGADDSET(&p->SignalDataBase.PendingSignalMask, Signal);
+
+ Pending = p->SignalDataBase.PendingSignalMask &
+ ~p->SignalDataBase.BlockedSignalMask;
+
+ //
+ // If the signal is defaulted and the default action is to ignore,
+ // add it to the pending set but don't interrupt the system call.
+ // This allows a process to block sigchld and then handle it later
+ // by changing the action and then unblocking.
+ //
+
+ if (Signal == SIGCHLD &&
+ p->SignalDataBase.SignalDisposition[Signal-1].sa_handler
+ == (_handler)SIG_DFL) {
+ ReleaseProcessLock(p);
+ return;
+ }
+
+
+ if (!Pending) {
+
+ // Fast path: no pending unblocked signals.
+
+ ReleaseProcessLock(p);
+ return;
+ }
+
+ if (p->InPsx > 0) {
+ if (Stopped == p->State && SIGKILL != Signal) {
+
+ //
+ // While a process is stopped, any additional signals that
+ // are sent to the process shall not be delivered until the
+ // process is continued except SIGKILL, which always terminates
+ // the receiving process. 1003.1-1990 3.3.1.3 (1b)
+ //
+
+ ReleaseProcessLock(p);
+ return;
+ }
+
+ // XXX.mjb: I'm tense about this code. The process being
+ // killed is InPsx, and we're unblocking him. But what if
+ // a processes is killing himself, or there is more than one
+ // api request thread and the other process is executing a
+ // system call, but is not blocked?
+
+ ReleaseProcessLock(p);
+ UnblockProcess(p, SignalInterrupt, FALSE, Signal);
+ return;
+ }
+
+ if (p->State == Unconnected) {
+ // leave the signal pending
+ ReleaseProcessLock(p);
+ return;
+ }
+
+ ReleaseProcessLock(p);
+
+ //
+ // Drag Process inside so that it looks at its signals. We set
+ // the NO_FORK bit to ensure that the process calls the NullApi
+ // before he calls fork. We want to avoid a situation where the
+ // process stack is modified by RtlRemoteCall while the process
+ // is blocked in lpc in fork(), and we copy his stack to a child
+ // process.
+ //
+
+ p->Flags |= P_NO_FORK;
+
+ Status = RtlRemoteCall(p->Process, p->Thread, (PVOID)p->NullApiCaller,
+ 0, NULL, TRUE, FALSE);
+
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("Dragging proc 0x%x inside for sig %d\n", p->Pid, Signal));
+ KdPrint(("PSXSS: RtlRemoteCall: 0x%x\n", Status));
+ }
+}
+
+static NTSTATUS
+InitProcNoParent(
+ PPSX_PROCESS NewProcess,
+ IN PPSX_SESSION Session OPTIONAL,
+ IN ULONG SessionId OPTIONAL
+ );
+
+static void
+InitProcFromParent(
+ PPSX_PROCESS NewProcess,
+ PPSX_PROCESS ForkProcess
+ );
+
+NTSTATUS
+PsxInitializeProcess(
+ IN PPSX_PROCESS NewProcess,
+ IN PPSX_PROCESS ForkProcess OPTIONAL,
+ IN ULONG SessionId OPTIONAL,
+ IN HANDLE ProcessHandle,
+ IN HANDLE ThreadHandle,
+ IN PPSX_SESSION Session OPTIONAL
+ )
+{
+ NTSTATUS Status;
+
+ NewProcess->IntControlBlock = (PINTCB)NULL;
+ NewProcess->State = Unconnected;
+ NewProcess->Flags = 0;
+ NewProcess->Process = ProcessHandle;
+ NewProcess->Thread = ThreadHandle;
+ NewProcess->AlarmTimer = NULL;
+ NewProcess->ProcessIsBeingDebugged = FALSE;
+
+ NewProcess->BlockingThread = (HANDLE)NULL;
+
+ RtlZeroMemory((PVOID)&NewProcess->ProcessTimes, sizeof(struct tms));
+
+ if (ARGUMENT_PRESENT(ForkProcess)) {
+ InitProcFromParent(NewProcess, ForkProcess);
+ Status = STATUS_SUCCESS;
+ } else {
+ Status = InitProcNoParent(NewProcess, Session, SessionId);
+ }
+ ReleaseProcessLock(NewProcess);
+
+ return Status;
+}
+
+static void
+InitProcFromParent(
+ PPSX_PROCESS NewProcess,
+ PPSX_PROCESS ForkProcess
+ )
+{
+ NTSTATUS Status;
+
+ //
+ // Propagate Process Attributes
+ //
+
+ //
+ // InPsx -- this new process will return directly to user-land,
+ // without the posix subsystem replying to his message. Therefore,
+ // we set InPsx to 0 here.
+ //
+
+ NewProcess->InPsx = 0;
+ NewProcess->ParentPid = ForkProcess->Pid;
+ NewProcess->ProcessGroupId = ForkProcess->ProcessGroupId;
+ InsertHeadList(&ForkProcess->GroupLinks,
+ &NewProcess->GroupLinks);
+
+ REFERENCE_PSX_SESSION(ForkProcess->PsxSession);
+ NewProcess->PsxSession = ForkProcess->PsxSession;
+
+ NewProcess->EffectiveUid = ForkProcess->EffectiveUid;
+ NewProcess->RealUid = ForkProcess->RealUid;
+ NewProcess->EffectiveGid = ForkProcess->EffectiveGid;
+ NewProcess->RealGid = ForkProcess->RealGid;
+ NewProcess->DirectoryPrefix = ForkProcess->DirectoryPrefix;
+ NewProcess->FileModeCreationMask = ForkProcess->FileModeCreationMask;
+
+ if (ForkProcess->ProcessIsBeingDebugged && PsxpDebuggerActive) {
+ Status = NtSetInformationProcess(NewProcess->Process,
+ ProcessDebugPort, (PVOID)&PsxpDebugPort,
+ sizeof(HANDLE));
+ if (NT_SUCCESS(Status)) {
+ NewProcess->ProcessIsBeingDebugged = TRUE;
+ NewProcess->DebugUiClientId = ForkProcess->DebugUiClientId;
+ }
+ }
+
+ //
+ // Fork the signal database
+ //
+
+ NewProcess->SignalDataBase = ForkProcess->SignalDataBase;
+ SIGEMPTYSET(&NewProcess->SignalDataBase.PendingSignalMask);
+
+ //
+ // Fork The Open File Table
+ //
+
+ ForkProcessFileTable(ForkProcess, NewProcess);
+ ReleaseProcessLock(ForkProcess);
+}
+
+
+static NTSTATUS
+InitProcNoParent(
+ PPSX_PROCESS NewProcess,
+ IN PPSX_SESSION Session,
+ IN ULONG SessionId OPTIONAL
+ )
+{
+ HANDLE TokenHandle = NULL;
+ TOKEN_PRIMARY_GROUP *pGroup = NULL;
+ TOKEN_GROUPS *pGroups = NULL;
+ PTOKEN_USER pTokenUser = NULL;
+ ULONG LengthNeeded;
+ PULONG pu;
+ PSID_AND_ATTRIBUTES pSA;
+ NTSTATUS Status;
+ PFILEDESCRIPTOR Fd;
+ ULONG i;
+ PSID SecondGroupChoice; // to be used if primary group is
+ // no good.
+ BOOLEAN PrimaryGroupOk; // is primary group good?
+
+ NewProcess->ParentPid = SPECIALPID;
+ NewProcess->ProcessGroupId = NewProcess->Pid; // process group
+ InitializeListHead(&NewProcess->GroupLinks);
+
+ //
+ // This process doesn't get to user-land by returning from an lpc
+ // api, so we take the opportunity to set InPsx to zero here.
+ //
+ NewProcess->InPsx = 0;
+
+ Session->SessionLeader = NewProcess->Pid;
+ NewProcess->PsxSession = Session;
+
+//XXX.mjb: is this really right? Or &= ~P_HAS_EXECED?
+ NewProcess->Flags |= P_HAS_EXECED;
+ NewProcess->DirectoryPrefix = NULL;
+
+ //
+ // Signal DataBase
+ //
+
+ SIGEMPTYSET(&NewProcess->SignalDataBase.BlockedSignalMask);
+ SIGEMPTYSET(&NewProcess->SignalDataBase.PendingSignalMask);
+ SIGEMPTYSET(&NewProcess->SignalDataBase.SigSuspendMask);
+ for (i = 0; i < _SIGMAXSIGNO; i++) {
+ NewProcess->SignalDataBase.SignalDisposition[i].sa_handler =
+ (_handler)SIG_DFL;
+ NewProcess->SignalDataBase.SignalDisposition[i].sa_flags = 0;
+ SIGEMPTYSET(&NewProcess->SignalDataBase.SignalDisposition[i].sa_mask);
+ }
+
+ Fd = NewProcess->ProcessFileTable;
+
+ RtlEnterCriticalSection(&SystemOpenFileLock);
+ {
+ for (i = 0; i < OPEN_MAX; i++, Fd++) {
+ Fd->SystemOpenFileDesc = (PSYSTEMOPENFILE)NULL;
+ Fd->Flags = 0;
+ }
+ } RtlLeaveCriticalSection(&SystemOpenFileLock);
+
+ //
+ // Examine the new process's token to figure out what the
+ // uid should be.
+ //
+
+ Status = NtOpenProcessToken(NewProcess->Process, GENERIC_READ,
+ &TokenHandle);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = NtQueryInformationToken(TokenHandle, TokenUser,
+ NULL, 0, &LengthNeeded);
+ ASSERT(STATUS_BUFFER_TOO_SMALL == Status);
+
+ pTokenUser = RtlAllocateHeap(PsxHeap, 0, LengthNeeded);
+ if (NULL == pTokenUser) {
+ Status = STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ Status = NtQueryInformationToken(TokenHandle, TokenUser,
+ (PVOID)pTokenUser, LengthNeeded, &LengthNeeded);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: NtQueryInfoToken: 0x%x\n", Status));
+ }
+ ASSERT(NT_SUCCESS(Status));
+
+ NewProcess->EffectiveUid = NewProcess->RealUid =
+ MakePosixId(pTokenUser->User.Sid);
+
+ //
+ // Figure out the primary group. Security allows the user to
+ // set his primary group to whatever he wants, so we make sure
+ // that his choice is reasonable. If his PrimaryGroup is in
+ // the group array, he's fine. If it's not, we make his
+ // PrimaryGroup the first group from the group array other
+ // than World. If there are none other than World, that's
+ // what he gets.
+ //
+
+ Status = NtQueryInformationToken(TokenHandle, TokenPrimaryGroup,
+ NULL, 0, &LengthNeeded);
+ ASSERT(STATUS_BUFFER_TOO_SMALL == Status);
+
+ pGroup = RtlAllocateHeap(PsxHeap, 0, LengthNeeded);
+ if (NULL == pGroup) {
+ NewProcess->EffectiveGid = NewProcess->RealGid = 0;
+ Status = STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ Status = NtQueryInformationToken(TokenHandle, TokenPrimaryGroup,
+ (PVOID)pGroup, LengthNeeded, &LengthNeeded);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = NtQueryInformationToken(TokenHandle, TokenGroups, NULL,
+ 0, &LengthNeeded);
+ ASSERT(STATUS_BUFFER_TOO_SMALL == Status);
+
+ pGroups = RtlAllocateHeap(PsxHeap, 0, LengthNeeded);
+ if (NULL == pGroups) {
+ NewProcess->EffectiveGid = NewProcess->RealGid = 0;
+ Status = STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ Status = NtQueryInformationToken(TokenHandle, TokenGroups,
+ (PVOID)pGroups,
+ LengthNeeded, &LengthNeeded);
+ ASSERT(NT_SUCCESS(Status));
+
+ SecondGroupChoice = NULL;
+ PrimaryGroupOk = FALSE;
+
+ for (i = 0; i < pGroups->GroupCount; ++i) {
+ if (RtlEqualSid(pGroups->Groups[i].Sid, pGroup->PrimaryGroup)) {
+ PrimaryGroupOk = TRUE;
+ }
+
+ //
+ // Our second group choice is the first group in the list
+ // that is not the World group.
+ //
+
+ if (NULL == SecondGroupChoice &&
+ SE_WORLD_POSIX_ID != MakePosixId(pGroups->Groups[i].Sid)) {
+ SecondGroupChoice = pGroups->Groups[i].Sid;
+ }
+ }
+
+ if (PrimaryGroupOk) {
+ NewProcess->EffectiveGid = NewProcess->RealGid =
+ MakePosixId(pGroup->PrimaryGroup);
+ } else if (NULL != SecondGroupChoice) {
+ NewProcess->EffectiveGid = NewProcess->RealGid =
+ MakePosixId(SecondGroupChoice);
+ } else {
+ NewProcess->EffectiveGid = NewProcess->RealGid =
+ SE_WORLD_POSIX_ID;
+ }
+
+out:
+ if (pGroup) RtlFreeHeap(PsxHeap, 0, (PVOID)pGroup);
+ if (pGroups) RtlFreeHeap(PsxHeap, 0, (PVOID)pGroups);
+ if (pTokenUser) RtlFreeHeap(PsxHeap, 0, (PVOID)pTokenUser);
+ if (TokenHandle) NtClose(TokenHandle);
+ return Status;
+}
+
+
+//
+// PsxCreateProcess -- this routine is called only by PsxCreateConSession
+// to create the first process for a brand-new session.
+//
+
+BOOLEAN
+PsxCreateProcess(
+ PPSX_EXEC_INFO ExecInfo,
+ OUT PPSX_PROCESS *NewProcess,
+ IN HANDLE ParentProcess,
+ IN PPSX_SESSION Session
+ )
+{
+ NTSTATUS Status;
+ UNICODE_STRING ImageFileName;
+ UNICODE_STRING Path;
+ UNICODE_STRING LibPath;
+ UNICODE_STRING CWD;
+ PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
+ RTL_USER_PROCESS_INFORMATION ProcessInformation;
+ PPSX_PROCESS Process;
+
+ Path.Buffer = NULL;
+ LibPath.Buffer = NULL;
+ CWD.Buffer = NULL;
+ Status = RtlAnsiStringToUnicodeString(&Path, &ExecInfo->Path, TRUE);
+ if (NT_SUCCESS(Status)) {
+ Status = RtlAnsiStringToUnicodeString(&LibPath, &ExecInfo->LibPath, TRUE);
+ if (NT_SUCCESS(Status)) {
+ Status = RtlAnsiStringToUnicodeString(&CWD, &ExecInfo->CWD, TRUE);
+ }
+ }
+
+ if (NT_SUCCESS(Status)) {
+ //
+ // Beware - ExecInfo->ArgV is used to pass uninterpreted binary
+ // data. It is NOT an ANSI string. So don't try to convert
+ // to Unicode. Just lie to RtlCreateProcessParameters.
+ //
+
+ Status = RtlCreateProcessParameters(&ProcessParameters, &Path,
+ &LibPath, &CWD, (PUNICODE_STRING)&ExecInfo->Argv, NULL,
+ NULL, NULL, NULL, NULL);
+ }
+
+ RtlFreeUnicodeString( &Path );
+ RtlFreeUnicodeString( &LibPath );
+ RtlFreeUnicodeString( &CWD );
+ if (!NT_SUCCESS(Status)) {
+ return FALSE; // ENOMEM
+ }
+ ProcessParameters->CurrentDirectory.Handle = NULL;
+
+ ImageFileName = ProcessParameters->ImagePathName;
+ ImageFileName.Buffer = (PWSTR)
+ ((PCHAR)ImageFileName.Buffer + (ULONG)ProcessParameters);
+
+ IF_PSX_DEBUG(EXEC) {
+ KdPrint(("PSXSS: Creating process %wZ\n", &ImageFileName));
+ }
+
+ Status = RtlCreateUserProcess(&ImageFileName, OBJ_CASE_INSENSITIVE,
+ ProcessParameters, NULL,
+ NULL, ParentProcess, TRUE, NULL, NULL, &ProcessInformation);
+
+ RtlDestroyProcessParameters(ProcessParameters);
+
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: RtlCreateUserProcess: 0x%x\n", Status));
+ return FALSE; // ENOEXEC
+ }
+
+ if (ProcessInformation.ImageInformation.SubSystemType !=
+ IMAGE_SUBSYSTEM_POSIX_CUI) {
+ NtClose(ProcessInformation.Process);
+ return FALSE; // ENOEXEC
+ }
+
+ Status = NtSetInformationProcess(ProcessInformation.Process,
+ ProcessExceptionPort, (PVOID)&PsxApiPort, sizeof(PsxApiPort));
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: NtSetInfoProc: 0x%x\n", Status));
+ }
+ ASSERT(NT_SUCCESS(Status));
+
+ {
+ ULONG HardErrorMode = 0; // disable popups
+ Status = NtSetInformationProcess(
+ ProcessInformation.Process,
+ ProcessDefaultHardErrorMode,
+ (PVOID)&HardErrorMode,
+ sizeof(HardErrorMode)
+ );
+ ASSERT(NT_SUCCESS(Status));
+ }
+
+ Process = PsxAllocateProcess(&ProcessInformation.ClientId);
+
+ if (!Process) {
+ Status = NtTerminateProcess(ProcessInformation.Process,
+ STATUS_SUCCESS);
+ ASSERT(NT_SUCCESS(Status));
+ NtClose(ProcessInformation.Process);
+ NtClose(ProcessInformation.Thread);
+ return FALSE; // EAGAIN
+ }
+
+ Status = PsxInitializeProcess(Process, NULL, 0L,
+ ProcessInformation.Process, ProcessInformation.Thread, Session);
+ if (!NT_SUCCESS(Status)) {
+ Status = NtTerminateProcess(ProcessInformation.Process,
+ STATUS_SUCCESS);
+ ASSERT(NT_SUCCESS(Status));
+ NtClose(ProcessInformation.Process);
+ NtClose(ProcessInformation.Thread);
+ return FALSE;
+ }
+
+ Process->InitialPebPsxData.Length = sizeof(Process->InitialPebPsxData);
+ Process->InitialPebPsxData.ClientStartAddress = NULL;
+
+ //
+ // Set the session port. The client constructs the port name from the
+ // unique id, opens the port, and sets the actual handle.
+ //
+
+ Process->InitialPebPsxData.SessionPortHandle =
+ (HANDLE)Process->PsxSession->Terminal->UniqueId;
+
+ PsxInitializeDirectories(Process, &ExecInfo->CWD);
+ *NewProcess = Process;
+
+ return TRUE;
+}
+
+PPSX_SESSION
+PsxAllocateSession(
+ IN PPSX_CONTROLLING_TTY Terminal OPTIONAL,
+ IN pid_t SessionLeader
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called to allocate and initialize a posix
+ session.
+
+ Each session may be associated with a controlling terminal. If
+ the Terminal parameter is specified, and if the terminal is
+ associated with a session, then no new session is created, and the
+ process simply joins the session as a new process group. This
+ case can only occur in a double handoff... (a foreign app is execed,
+ and then it execs a POSIX app with a terminal stdin that is the same
+ as the one it left with.
+
+Arguments:
+
+ Terminal - An optional parameter, that if supplied specifies the
+ controlling terminal to be associated with the session.
+
+ SessionLeader - Supplies the process id of the session leader for
+ this session.
+
+Return Value:
+
+ Returns a pointer to the new session.
+
+--*/
+
+{
+ PPSX_SESSION Session;
+
+ if (ARGUMENT_PRESENT(Terminal)) {
+ //
+ // Look inside terminal to see if it is associated with a session.
+ // If not, then create a session attached to the terminal
+ // (logon to posix). If it is associated with a session, then become
+ // a new group in the session and dis-regard SessionLeader parameter
+ //
+
+ ;
+ }
+
+ //
+ // Allocate storage for the session and initialize the session
+ //
+
+ Session = RtlAllocateHeap(PsxHeap, 0,sizeof(PSX_SESSION));
+ if (NULL == Session) {
+ return NULL;
+ }
+ Session->ReferenceCount = 1;
+ Session->SessionLeader = SessionLeader;
+ Session->Terminal = Terminal;
+
+ if (ARGUMENT_PRESENT(Terminal)) {
+ Terminal->ForegroundProcessGroup = SessionLeader;
+ Terminal->Session = Session;
+ }
+ return Session;
+}
+
+VOID
+PsxDeallocateSession(
+ IN PPSX_SESSION Session
+ )
+
+/*++
+
+Routine Description:
+
+ This function deallocates a posix session and frees the terminal
+ so that it can become associated with new sessions.
+
+Arguments:
+
+ Session - Supplies the address of the posix session being deallocated
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ if (Session->Terminal) {
+ Session->Terminal->ForegroundProcessGroup = SPECIALPID;
+ Session->Terminal->Session = NULL;
+
+ RtlDeleteCriticalSection(&Session->Terminal->Lock);
+
+ if (NULL != Session->Terminal->IoBuffer) {
+ Status = NtUnmapViewOfSection(NtCurrentProcess(),
+ Session->Terminal->IoBuffer);
+ ASSERT(NT_SUCCESS(Status));
+ }
+
+ RtlFreeHeap(PsxHeap, 0, (PVOID)Session->Terminal);
+ }
+
+ UnlockNtSessionList();
+ RtlFreeHeap(PsxHeap, 0, (PVOID)Session);
+}
+
+VOID
+PsxTerminateProcessBySignal(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m,
+ IN ULONG Signal
+ )
+{
+ UNREFERENCED_PARAMETER(m);
+
+ Exit(p, Signal);
+}
+
+VOID
+Exit(
+ IN PPSX_PROCESS p,
+ IN ULONG ExitStatus
+ )
+
+/*++
+
+Routine Description:
+
+ This function process termination. It is called either
+ from PsxExit as a result of an applications call to _exit(),
+ or from within PSX to terminate a process.
+
+
+Arguments:
+
+ p - Supplies the address of the exiting process
+
+ ExitStatus - Supplies the exit status for the exiting process
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PPSX_PROCESS cp, WaitingParent,Parent;
+ PPSX_PROCESS FirstMember, NextMember;
+ PLIST_ENTRY Next;
+ NTSTATUS Status;
+ HANDLE AlarmTimer;
+ BOOLEAN OrphanedAGroupRelatedChild, AlreadyOrphaned, PreviousTimerState;
+ KERNEL_USER_TIMES ProcessTime;
+ BOOLEAN WaitForProcess;
+
+ if ((ULONG)-1 == ExitStatus) {
+ //
+ // This process is dying because of some extraordinary event,
+ // and we should make sure that we clean up no matter what.
+ // XXX.mjb: ExitStatus could be different and more informative,
+ // but I haven't had a chance to test that.
+ //
+
+ WaitForProcess = FALSE;
+ ExitStatus = 0;
+ } else {
+ WaitForProcess = TRUE;
+ }
+
+
+ AcquireProcessLock(p);
+ if (Exited == p->State) {
+ ReleaseProcessLock(p);
+ return;
+ }
+ p->State = Exited;
+ p->Flags &= ~P_WAITED; // proc may satisfy wait
+
+ //
+ // The goal is that after we've changed the process state, no one else
+ // will try to muck with this process. So we should be able to
+ // release the process lock now.
+ //
+
+ ReleaseProcessLock(p);
+
+ p->ExitStatus = ExitStatus;
+
+ WaitingParent = NULL;
+ Parent = NULL;
+ OrphanedAGroupRelatedChild = FALSE;
+
+ //
+ // If this is a local directory prefix, then deallocate it
+ //
+
+ if (!IS_DIRECTORY_PREFIX_REMOTE(p->DirectoryPrefix)) {
+ RtlFreeHeap(PsxHeap, 0,
+ (PVOID)p->DirectoryPrefix->NtCurrentWorkingDirectory.Buffer);
+ if (0 != p->DirectoryPrefix->PsxCurrentWorkingDirectory.Length)
+ RtlFreeHeap(PsxHeap, 0,
+ (PVOID)p->DirectoryPrefix->PsxCurrentWorkingDirectory.Buffer);
+ RtlFreeHeap(PsxHeap, 0,(PVOID)p->DirectoryPrefix->PsxRoot.Buffer);
+ RtlFreeHeap(PsxHeap, 0,(PVOID)p->DirectoryPrefix);
+ }
+
+ //
+ // Lock the process table so that we can clear process' AlarmTimer
+ // interlocked with AlarmApcRoutine. Can not use ProcessLock since
+ // it is deallocated during process exit.
+ //
+ // REVISIT deallocation of process lock...
+ //
+
+ AcquireProcessStructureLock();
+
+ //
+ // Cancel and close alarm timer if present
+ //
+
+ if (p->AlarmTimer) {
+ AlarmTimer = p->AlarmTimer;
+ p->AlarmTimer = NULL;
+
+ //
+ // Unlock process table while doing timer cleanup
+ //
+
+ ReleaseProcessStructureLock();
+
+ Status = NtCancelTimer(AlarmTimer, &PreviousTimerState);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: NtCancelTimer: 0x%x\n", Status));
+ }
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = NtClose(AlarmTimer);
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Lock the process table so that we can scan process table.
+ //
+
+ AcquireProcessStructureLock();
+ }
+
+ //
+ // Scan process table looking for children, and job control
+ // related processes.
+ //
+
+ AlreadyOrphaned = IsGroupOrphaned(p->ProcessGroupId);
+
+ for (cp = FirstProcess; cp < LastProcess; cp++) {
+
+ //
+ // Only look at non-free slots
+ //
+
+ if (cp->Flags & P_FREE) {
+ continue;
+ }
+
+ if (cp->ParentPid == p->Pid) {
+
+ //
+ // Orphan and send SIGHUP to each child process
+ //
+
+ cp->ParentPid = SPECIALPID;
+ if (cp->State == Exited) {
+
+ //
+ // Orphaning an exited process is almost like an
+ // implicit wait in the sense that process resources
+ // are freed immediately.
+ //
+
+ cp->Flags |= P_FREE;
+
+ //
+ // Remove Process from CLIENT_ID Hash Table
+ //
+
+ RemoveEntryList(&cp->ClientIdHashLinks);
+ ASSERT(NULL != cp);
+
+ try {
+ RtlDeleteCriticalSection(&cp->ProcessLock);
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ KdPrint(("PSXSS: took a fault\n"));
+ }
+
+ } else {
+ if (cp->ProcessGroupId == p->ProcessGroupId) {
+ OrphanedAGroupRelatedChild = TRUE;
+ }
+ // PsxSignalProcess(cp, SIGHUP);
+ }
+ }
+ }
+
+ if (p->ParentPid != SPECIALPID) {
+ //
+ // The process has a parent. Process termination could satisfy a
+ // wait() or waitpid(). Parent must also be signaled.
+ //
+
+
+ Parent = PIDTOPROCESS(p->ParentPid);
+
+ //
+ // See if parent is waiting. Arange for wait completion...
+ //
+
+ if (Parent->State == Waiting) {
+ WaitingParent = Parent;
+ }
+
+ PsxSignalProcess(Parent, SIGCHLD);
+ }
+
+ //
+ // Check to see if the exiting processes group is already orphaned.
+ // If so, then do not do anything. Otherwise, remove him from the
+ // group list and then whip through the group. If any stopped processes
+ // are found, then SIGCONT, SIGHUP all processes in the group
+ //
+
+ LockNtSessionList();
+
+ FirstMember = CONTAINING_RECORD(p->GroupLinks.Flink, PSX_PROCESS,
+ GroupLinks);
+ RemoveEntryList(&p->GroupLinks);
+ InitializeListHead(&p->GroupLinks);
+
+ if (!AlreadyOrphaned && FirstMember != p) {
+
+ //
+ // See if exit causes group to be orphaned. This is the
+ // case if the exiting process is an orphan, or if the
+ // parent is in a different session.
+ //
+
+ if ( OrphanedAGroupRelatedChild ||
+ (Parent == NULL) ||
+ (Parent != NULL && Parent->PsxSession != p->PsxSession) ) {
+
+ //
+ // Scan the group. Looking for stopped processes. If a stopped
+ // process is found, then for each process in the group,
+ // send a SIGCONT followed by a SIGHUP.
+ //
+
+ NextMember = FirstMember;
+ do {
+ if (NextMember->State == Stopped) {
+ //
+ // A stopped group member was found. Scan the group
+ // and SIGCONT, SIGHUP each member
+ //
+
+ NextMember = FirstMember;
+ do {
+ Next = NextMember->GroupLinks.Flink;
+ PsxSignalProcess(NextMember, SIGCONT);
+ PsxSignalProcess(NextMember, SIGHUP);
+ NextMember = CONTAINING_RECORD(Next, PSX_PROCESS,
+ GroupLinks);
+ } while (NextMember != FirstMember);
+ break;
+ }
+ Next = NextMember->GroupLinks.Flink;
+ NextMember = CONTAINING_RECORD(Next, PSX_PROCESS, GroupLinks);
+ } while (NextMember != FirstMember);
+ }
+ }
+
+ UnlockNtSessionList();
+
+ //
+ // Unlock process table here; we depend on having just one api
+ // thread, so no one can call fork while we're still closing the
+ // files and so forth.
+ //
+
+ ReleaseProcessStructureLock();
+
+ //
+ // Now just close files, deallocate the session, close the thread,
+ // terminate the process, and close the process.
+ //
+
+ CloseProcessFileTable(p);
+
+ if (!(p->Flags & P_FOREIGN_EXEC)) {
+
+ //
+ // When a foreign process has exited, it's responsible
+ // for shooting its own threads.
+ //
+
+ Status = NtTerminateThread(p->Thread, ExitStatus);
+#ifdef PSX_MORE_ERRORS
+ if (!NT_SUCCESS(Status)) {
+ // XXX.mjb: this may fail if the thread has already died
+ // for some reason, f.e. been shot by pview.
+ KdPrint(("PSXSS: NtTerminateThread: 0x%x\n", Status));
+ }
+#endif
+ }
+
+ p->Flags &= ~P_FOREIGN_EXEC;
+
+ //
+ // We like to call NtWaitForSingle object on the thread handle,
+ // but we don't do that if the process has
+ // died in some unusual way, like the dll init routine failed.
+ //
+
+ if (WaitForProcess) {
+ Status = NtWaitForSingleObject(p->Thread, TRUE, NULL);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: Exit: NtWait: 0x%x\n", Status));
+ }
+ }
+
+ Status = NtClose(p->Thread);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: NtClose dead thread: 0x%x\n", Status));
+ }
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // We like to wait on the process handle here, sometimes we don't
+ // do that, see above.
+ //
+
+ if (WaitForProcess) {
+ Status = NtWaitForSingleObject(p->Process, TRUE, NULL);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: Exit: NtWait: 0x%x\n", Status));
+ }
+ }
+
+ //
+ // Get the time for this process and add to the accumulated time for
+ // the process
+ //
+
+ Status = NtQueryInformationProcess(p->Process, ProcessTimes,
+ (PVOID)&ProcessTime, sizeof(KERNEL_USER_TIMES), NULL);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: NtQueryInfoProc: 0x%x\n", Status));
+ } else {
+ ULONG Remainder;
+ ULONG PosixTime;
+
+ PosixTime = RtlExtendedLargeIntegerDivide(
+ ProcessTime.KernelTime, 10000, &Remainder).LowPart;
+ p->ProcessTimes.tms_stime += PosixTime;
+
+ PosixTime = RtlExtendedLargeIntegerDivide(
+ ProcessTime.UserTime, 10000, &Remainder).LowPart;
+ p->ProcessTimes.tms_utime += PosixTime;
+ }
+
+ NtClose(p->Process);
+
+ DEREFERENCE_PSX_SESSION(p->PsxSession, ExitStatus >> 8);
+
+ Status = NtClose(p->ClientPort);
+#ifdef PSX_MORE_ERRORS
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: NtClose: 0x%x\n", Status));
+ }
+#endif
+
+ //
+ // Remove the process from ClientIdHashTable; this must be done
+ // now rather than in wait(), because unless we do it now a new
+ // process could be created with the same ClientId as this zombie.
+ // This process shouldn't be making any more api requests, and
+ // that's the only time we look processes up by ClientId (right?).
+ //
+
+ RemoveEntryList(&p->ClientIdHashLinks);
+ InitializeListHead(&p->ClientIdHashLinks);
+
+ if (p->ParentPid == SPECIALPID) {
+ //
+ // Parent won't clean up after wait, so do all clean up here
+ //
+ p->Flags |= P_FREE;
+
+ //
+ // REVISIT... This might have to stay around
+ //
+
+ RtlDeleteCriticalSection(&p->ProcessLock);
+ }
+
+ if (NULL != WaitingParent) {
+ UnblockProcess(WaitingParent, WaitSatisfyInterrupt, FALSE, 0);
+ }
+}
+
+PSZ PsxpDefaultRoot = "\\DosDevices\\C:";
+
+VOID
+PsxInitializeDirectories(
+ IN PPSX_PROCESS Process,
+ IN PANSI_STRING pCwd
+ )
+{
+ PPSX_DIRECTORY_PREFIX DirectoryPrefix;
+ PSZ Root;
+ char sbWorkingDirectory[512];
+ char sbRoot[512];
+
+ (void)strcpy(sbWorkingDirectory, "\\DosDevices\\");
+ (void)strcat(sbWorkingDirectory, pCwd->Buffer);
+
+ (void)strcpy(sbRoot, sbWorkingDirectory);
+ sbRoot[strlen(PsxpDefaultRoot)] = '\0';
+
+ Root = sbRoot;
+
+ //
+ // The current working directory should end in a "\". We add one
+ // if it isn't there already.
+ //
+
+ if ('\\' != sbWorkingDirectory[strlen(sbWorkingDirectory) - 1]) {
+ (void)strcat(sbWorkingDirectory, "\\");
+ }
+
+ DirectoryPrefix = RtlAllocateHeap(PsxHeap, 0,sizeof(*DirectoryPrefix));
+ ASSERT(NULL != DirectoryPrefix);
+
+ DirectoryPrefix->NtCurrentWorkingDirectory.Buffer =
+ RtlAllocateHeap(PsxHeap, 0, strlen(sbWorkingDirectory) + 1);
+
+ DirectoryPrefix->NtCurrentWorkingDirectory.Length =
+ (USHORT)strlen(sbWorkingDirectory);
+
+ DirectoryPrefix->NtCurrentWorkingDirectory.MaximumLength =
+ DirectoryPrefix->NtCurrentWorkingDirectory.Length + 1;
+
+ RtlMoveMemory(DirectoryPrefix->NtCurrentWorkingDirectory.Buffer,
+ sbWorkingDirectory,
+ DirectoryPrefix->NtCurrentWorkingDirectory.MaximumLength);
+
+ DirectoryPrefix->PsxCurrentWorkingDirectory.Length = 0;
+
+ DirectoryPrefix->PsxRoot.Buffer = RtlAllocateHeap(PsxHeap, 0,
+ strlen(Root) + 1);
+ ASSERT(DirectoryPrefix->PsxRoot.Buffer);
+
+ DirectoryPrefix->PsxRoot.Length = (USHORT)strlen(Root);
+
+ DirectoryPrefix->PsxRoot.MaximumLength =
+ DirectoryPrefix->PsxRoot.Length + 1;
+
+ RtlMoveMemory(DirectoryPrefix->PsxRoot.Buffer, Root,
+ DirectoryPrefix->PsxRoot.MaximumLength);
+
+ Process->DirectoryPrefix = DirectoryPrefix;
+}
+
+//
+// PsxPropagateDirectories --
+//
+// Read the root directory, the current working directory
+// from an existing process.
+//
+BOOLEAN
+PsxPropagateDirectories(
+ IN PPSX_PROCESS Process
+ )
+{
+ NTSTATUS Status;
+ PPSX_DIRECTORY_PREFIX Prefix = NULL;
+ STRING ClientPrefix;
+ ULONG BytesRead;
+ PVOID p;
+
+ Prefix = RtlAllocateHeap(PsxHeap, 0, sizeof(*Prefix));
+ if (NULL == Prefix) {
+ goto ErrorExit;
+ }
+ Prefix->PsxRoot.Buffer = NULL;
+ Prefix->NtCurrentWorkingDirectory.Buffer = NULL;
+
+ Status = NtReadVirtualMemory(Process->Process,
+ (PVOID)MAKE_DIRECTORY_PREFIX_VALID(Process->DirectoryPrefix),
+ (PVOID)Prefix, sizeof(*Prefix), &BytesRead);
+
+ if (!NT_SUCCESS(Status)) {
+ goto ErrorExit;
+ }
+
+ //
+ // Capture the Root and NtCurrentWorkingDirectory
+ //
+ // XXX.mjb: TODO sanity check lengths ?
+ //
+
+ ClientPrefix = Prefix->PsxRoot;
+
+ p = RtlAllocateHeap(PsxHeap, 0, ClientPrefix.Length);
+ if (NULL == p) {
+ goto ErrorExit;
+ }
+
+ Prefix->PsxRoot.Buffer = p;
+
+ Status = NtReadVirtualMemory(Process->Process, ClientPrefix.Buffer,
+ Prefix->PsxRoot.Buffer, ClientPrefix.Length, &BytesRead);
+ if (!NT_SUCCESS(Status)) {
+ goto ErrorExit;
+ }
+
+ Prefix->PsxRoot.Length = ClientPrefix.Length;
+ Prefix->PsxRoot.MaximumLength = ClientPrefix.Length;
+
+ ClientPrefix = Prefix->NtCurrentWorkingDirectory;
+
+ p = RtlAllocateHeap(PsxHeap, 0, ClientPrefix.Length);
+ if (NULL == p) {
+ goto ErrorExit;
+ }
+
+ Prefix->NtCurrentWorkingDirectory.Buffer = p;
+
+ Status = NtReadVirtualMemory(Process->Process, ClientPrefix.Buffer,
+ Prefix->NtCurrentWorkingDirectory.Buffer,
+ ClientPrefix.Length, &BytesRead);
+ if (!NT_SUCCESS(Status)) {
+ goto ErrorExit;
+ }
+
+ Prefix->NtCurrentWorkingDirectory.Length = ClientPrefix.Length;
+ Prefix->NtCurrentWorkingDirectory.MaximumLength = ClientPrefix.Length;
+
+ Prefix->PsxCurrentWorkingDirectory.Buffer = NULL;
+ Prefix->PsxCurrentWorkingDirectory.Length = 0;
+ Prefix->PsxCurrentWorkingDirectory.MaximumLength = 0;
+
+ Process->DirectoryPrefix = Prefix;
+ return TRUE;
+
+ErrorExit:
+
+ if (NULL != Prefix->NtCurrentWorkingDirectory.Buffer) {
+ RtlFreeHeap(PsxHeap, 0, (PVOID)Prefix->NtCurrentWorkingDirectory.Buffer);
+ }
+ if (NULL != Prefix->PsxRoot.Buffer) {
+ RtlFreeHeap(PsxHeap, 0, (PVOID)Prefix->PsxRoot.Buffer);
+ }
+ if (NULL != Prefix) {
+ RtlFreeHeap(PsxHeap, 0, (PVOID)Prefix);
+ }
+ return FALSE;
+}
+
+//
+// When we allocate space in PsxPropagateDirectories, and then find
+// that we can't use it, we free it with PsxFreeDirectories.
+//
+VOID
+PsxFreeDirectories(
+ IN PPSX_PROCESS p
+ )
+{
+ PPSX_DIRECTORY_PREFIX Prefix;
+
+ Prefix = p->DirectoryPrefix;
+
+ if (NULL != Prefix->NtCurrentWorkingDirectory.Buffer) {
+ RtlFreeHeap(PsxHeap, 0, Prefix->NtCurrentWorkingDirectory.Buffer);
+ }
+ if (NULL != Prefix->PsxRoot.Buffer) {
+ RtlFreeHeap(PsxHeap, 0, (PVOID)Prefix->PsxRoot.Buffer);
+ }
+ if (NULL != Prefix) {
+ RtlFreeHeap(PsxHeap, 0, (PVOID)Prefix);
+ }
+}
+
+/*++
+
+Routine Description:
+
+ Locate a Posix Session by it's Unique Id.
+
+Arguments:
+
+ UniqueId - the session's unique id.
+
+Return Value:
+
+ NULL - no such session could be found.
+
+ Non-NULL - the found session.
+
+--*/
+
+PPSX_SESSION
+PsxLocateSessionByUniqueId(
+ ULONG UniqueId
+ )
+{
+ PPSX_PROCESS Process;
+
+ //
+ // We don't have a list of sessions at the moment, so we
+ // linear-scan the process table, checking the session of
+ // each process to see if it's the one we want.
+ //
+
+ AcquireProcessStructureLock();
+
+ for (Process = FirstProcess; Process < LastProcess; Process++) {
+ if (Process->Flags & P_FREE) {
+ continue;
+ }
+
+ if (NULL == Process->PsxSession->Terminal)
+ continue;
+
+ if (Process->PsxSession->Terminal->UniqueId == UniqueId) {
+ ReleaseProcessStructureLock();
+ return Process->PsxSession;
+ }
+ }
+
+ ReleaseProcessStructureLock();
+ return NULL;
+}
diff --git a/private/posix/psxss/psxss.c b/private/posix/psxss/psxss.c
new file mode 100644
index 000000000..86a40d9c1
--- /dev/null
+++ b/private/posix/psxss/psxss.c
@@ -0,0 +1,112 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ psxss.c
+
+Abstract:
+
+ This is the main startup module for the POSIX Emulation Subsystem Server
+
+Author:
+
+ Steve Wood (stevewo) 22-Aug-1989
+
+Environment:
+
+ User Mode Only
+
+Revision History:
+
+--*/
+
+#include "psxsrv.h"
+
+#if DBG
+ULONG PsxDebug = 0;
+#endif //DBG
+
+extern NTSTATUS PsxServerInitialization(VOID);
+
+int
+_CRTAPI1
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ STRING DirectoryName;
+ UNICODE_STRING DirectoryName_U;
+ CHAR localSecurityDescriptor[SECURITY_DESCRIPTOR_MIN_LENGTH];
+ PSECURITY_DESCRIPTOR securityDescriptor;
+ NTSTATUS Status;
+
+ UNREFERENCED_PARAMETER(argc);
+ UNREFERENCED_PARAMETER(argv);
+
+ //
+ // Create a root directory in the object name space that will be used
+ // to contain all of the named objects created by the POSIX Emulation
+ // subsystem.
+ //
+
+ RtlInitUnicodeString(&DirectoryName_U, PSX_SS_ROOT_OBJECT_DIRECTORY);
+
+ Status = RtlCreateSecurityDescriptor((PSECURITY_DESCRIPTOR)
+ &localSecurityDescriptor,
+ SECURITY_DESCRIPTOR_REVISION);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = RtlSetDaclSecurityDescriptor((PSECURITY_DESCRIPTOR)
+ &localSecurityDescriptor,
+ TRUE,
+ (PACL) NULL,
+ FALSE);
+
+ securityDescriptor = (PSECURITY_DESCRIPTOR)&localSecurityDescriptor;
+
+ InitializeObjectAttributes(&ObjectAttributes,
+ &DirectoryName_U, OBJ_PERMANENT, NULL, securityDescriptor);
+
+ Status = NtCreateDirectoryObject(&PsxRootDirectory,
+ DIRECTORY_ALL_ACCESS,
+ &ObjectAttributes);
+ if (!NT_SUCCESS(Status)) {
+ IF_DEBUG {
+ KdPrint(("PSXSS: Unable to initialize server; Status == %X\n",
+ Status));
+ }
+ goto out;
+ }
+
+ //
+ // Initialize the PSX Server Session Manager API Port, the listen thread
+ // one request thread.
+ //
+ Status = PsxSbApiPortInitialize();
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Connect to the session manager so we can start foreign sessions
+ //
+ Status = SmConnectToSm(&PsxSbApiPortName_U,
+ PsxSbApiPort,
+ IMAGE_SUBSYSTEM_POSIX_CUI,
+ &PsxSmApiPort);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = PsxServerInitialization();
+ if (!NT_SUCCESS(Status)) {
+ IF_PSX_DEBUG(INIT) {
+ KdPrint(("PSXSS: Unable to initialize; Status = %X\n", Status));
+ }
+ NtTerminateProcess(NtCurrentProcess(), Status);
+ }
+
+out:
+ NtTerminateThread(NtCurrentThread(), STATUS_SUCCESS);
+ return 0;
+}
diff --git a/private/posix/psxss/psxss.rc b/private/posix/psxss/psxss.rc
new file mode 100644
index 000000000..5181a939a
--- /dev/null
+++ b/private/posix/psxss/psxss.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Posix Subsystem Application"
+#define VER_INTERNALNAME_STR "psxss.exe"
+
+#include "common.ver"
+
+
diff --git a/private/posix/psxss/psxsup.c b/private/posix/psxss/psxsup.c
new file mode 100644
index 000000000..0e8db44c8
--- /dev/null
+++ b/private/posix/psxss/psxsup.c
@@ -0,0 +1,850 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ psxsup.c
+
+Abstract:
+
+ PSX Support Routines
+
+Author:
+
+ Mark Lucovsky (markl) 27-Nov-1989
+
+Revision History:
+
+--*/
+
+#include "psxsrv.h"
+#include <ntlsa.h>
+#include <ntsam.h>
+#include <ntseapi.h>
+
+#include "seposix.h"
+
+#define UNICODE
+#include <windows.h>
+#include <lm.h>
+#include <lmaccess.h>
+
+#include <sys/stat.h>
+
+//
+// This number will never be returned in the PosixOffset field of
+// a trusted domain query.
+//
+#define INVALID_POSIX_OFFSET 1
+
+ULONG
+PsxStatusToErrno(
+ IN NTSTATUS Status
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure converts an NT status code to an
+ equivalent errno value.
+
+ The conversion is a function of the status code class.
+
+Arguments:
+
+ Class - Supplies the status code class to use.
+
+ Status - Supplies the status code to convert.
+
+Return Value:
+
+ Returns an equivalent error code to the supplied status code.
+
+--*/
+
+{
+ ULONG Error;
+
+ switch (Status) {
+
+ case STATUS_INVALID_PARAMETER:
+ Error = EINVAL;
+ break;
+
+ case STATUS_DIRECTORY_NOT_EMPTY:
+ // Error = ENOTEMPTY;
+ Error = EEXIST;
+ break;
+
+ case STATUS_OBJECT_PATH_INVALID:
+ case STATUS_OBJECT_PATH_SYNTAX_BAD:
+ case STATUS_NOT_A_DIRECTORY:
+ Error = ENOTDIR;
+ break;
+
+ case STATUS_OBJECT_NAME_COLLISION:
+ Error = EEXIST;
+ break;
+
+ case STATUS_OBJECT_PATH_NOT_FOUND:
+ case STATUS_OBJECT_NAME_NOT_FOUND:
+ case STATUS_DELETE_PENDING:
+ case STATUS_NO_SUCH_FILE:
+ Error = ENOENT;
+ break;
+
+ case STATUS_NO_MEMORY:
+ case STATUS_INSUFFICIENT_RESOURCES:
+ Error = ENOMEM;
+ break;
+
+ case STATUS_CANNOT_DELETE:
+ Error = ETXTBUSY;
+ break;
+
+ case STATUS_DISK_FULL:
+ Error = ENOSPC;
+ break;
+
+ case STATUS_MEDIA_WRITE_PROTECTED:
+ Error = EROFS;
+ break;
+
+ case STATUS_OBJECT_NAME_INVALID:
+ Error = ENAMETOOLONG;
+ break;
+
+ case STATUS_FILE_IS_A_DIRECTORY:
+ Error = EISDIR;
+ break;
+
+ case STATUS_NOT_SAME_DEVICE:
+ Error = EXDEV;
+ break;
+
+ case STATUS_INVALID_OWNER:
+ Error = EPERM;
+ break;
+
+ case STATUS_INVALID_IMAGE_FORMAT:
+ case STATUS_INVALID_IMAGE_LE_FORMAT:
+ case STATUS_INVALID_IMAGE_NOT_MZ:
+ case STATUS_INVALID_IMAGE_PROTECT:
+ case STATUS_INVALID_IMAGE_WIN_16:
+ Error = ENOEXEC;
+ break;
+
+ case STATUS_NOT_IMPLEMENTED:
+ Error = ENOSYS;
+ break;
+
+ case STATUS_TOO_MANY_LINKS:
+ Error = EMLINK;
+ break;
+
+ default:
+ Error = EACCES;
+ }
+
+ return Error;
+}
+
+ULONG
+PsxStatusToErrnoPath(
+ IN PUNICODE_STRING Path
+ )
+/*++
+
+Routine Description:
+
+ This procedure is called when an NtOpenFile returns
+ STATUS_OBJECT_PATH_NOT_FOUND; this routine is to
+ distinguish the following too cases:
+
+ /file.c/foo, where file.c exists (ENOTDIR)
+ /noent/foo, where noent doesn't exist (ENOENT)
+
+ (NtOpenFile returns OBJECT_PATH_NOT_FOUND for both cases).
+
+Arguments:
+
+ Path - Supplies the path that was given to NtOpenFile. The
+ path string is destroyed by this function.
+
+Return Value:
+
+ Returns an equivalent error code to the supplied status code.
+
+--*/
+{
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES Obj;
+ HANDLE FileHandle;
+ ULONG DesiredAccess;
+ IO_STATUS_BLOCK Iosb;
+ ULONG Options;
+ PWCHAR pwc, pwcSav;
+ ULONG MinLen = sizeof(L"\\DosDevices\\X:\\");
+
+ DesiredAccess = SYNCHRONIZE;
+ Options = FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE;
+
+ pwcSav = NULL;
+
+ for (;;) {
+ //
+ // Remove a trailing component.
+ //
+
+ pwc = wcsrchr(Path->Buffer, L'\\');
+
+ if (pwcSav)
+ *pwcSav = L'\\';
+
+ if (NULL == pwc) {
+ break;
+ }
+ *pwc = UNICODE_NULL;
+ pwcSav = pwc;
+
+ Path->Length = wcslen(Path->Buffer) * sizeof(WCHAR);
+
+ if (Path->Length <= MinLen) {
+ *pwcSav = L'\\';
+ break;
+ }
+
+ InitializeObjectAttributes(&Obj, Path, 0, NULL, NULL);
+
+ Status = NtOpenFile(&FileHandle, DesiredAccess, &Obj,
+ &Iosb, SHARE_ALL, Options);
+ if (NT_SUCCESS(Status)) {
+ NtClose(FileHandle);
+ }
+
+ if (STATUS_NOT_A_DIRECTORY == Status) {
+ *pwcSav = L'\\';
+ Path->Length = wcslen(Path->Buffer) * sizeof(WCHAR);
+ return ENOTDIR;
+ }
+ }
+ Path->Length = wcslen(Path->Buffer) * sizeof(WCHAR);
+ return ENOENT;
+}
+
+ULONG
+PsxDetermineFileClass(
+ IN HANDLE FileHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function examines a file handle and returns its FileClass
+
+Arguments:
+
+ FileHandle - Supplies a handle to an open file whose class is to be
+ determined.
+
+Return Value:
+
+ The file class of the specified file. Defined in <sys/stat.h>.
+
+--*/
+
+{
+ NTSTATUS st;
+ IO_STATUS_BLOCK Iosb;
+ FILE_BASIC_INFORMATION BasicInfo;
+ FILE_FS_DEVICE_INFORMATION DeviceInfo;
+
+ //
+ // Call NtQueryFile to get device type and attributes
+ //
+
+ st = NtQueryInformationFile(
+ FileHandle,
+ &Iosb,
+ &BasicInfo,
+ sizeof(BasicInfo),
+ FileBasicInformation
+ );
+ if (!NT_SUCCESS(st)) {
+ // XXX.mjb: Sometimes fails on HPFS
+ KdPrint(("PSXS: PsxDetermineFileClass: NtQueryInfoFile: 0x%x\n", st));
+ return S_IFREG;
+ }
+
+ st = NtQueryVolumeInformationFile(
+ FileHandle,
+ &Iosb,
+ &DeviceInfo,
+ sizeof(DeviceInfo),
+ FileFsDeviceInformation
+ );
+
+ ASSERT(NT_SUCCESS(st));
+
+ switch (DeviceInfo.DeviceType) {
+
+ case FILE_DEVICE_DATALINK:
+ case FILE_DEVICE_KEYBOARD:
+ case FILE_DEVICE_MOUSE:
+ case FILE_DEVICE_NETWORK:
+ case FILE_DEVICE_NULL:
+ case FILE_DEVICE_PHYSICAL_NETCARD:
+ case FILE_DEVICE_PARALLEL_PORT:
+ case FILE_DEVICE_PRINTER:
+ case FILE_DEVICE_SOUND:
+ case FILE_DEVICE_SCREEN:
+ case FILE_DEVICE_SERIAL_PORT:
+ case FILE_DEVICE_TRANSPORT:
+ return S_IFCHR;
+
+ case FILE_DEVICE_DFS:
+ case FILE_DEVICE_DISK_FILE_SYSTEM:
+ case FILE_DEVICE_NETWORK_FILE_SYSTEM:
+ return S_IFBLK;
+
+ case FILE_DEVICE_DISK:
+ case FILE_DEVICE_VIRTUAL_DISK:
+ case FILE_DEVICE_TAPE:
+ break;
+
+ default:
+ break;
+ // return 0;
+ }
+
+ //
+ // The only thing left is RegularFile class. Now
+ // determine if this is a directory, named pipe,
+ // or regular file.
+ //
+
+ if (BasicInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ return S_IFDIR;
+ }
+
+ //
+ // For now, anything marked as a system file is a named pipe.
+ // In the future, this will involve checking to see if file
+ // has the Object Bit and has the appropriate EA (named pipe
+ // class id).
+ //
+
+ if (BasicInfo.FileAttributes & FILE_ATTRIBUTE_SYSTEM) {
+ return S_IFIFO;
+ }
+
+ return S_IFREG;
+
+}
+
+VOID
+EndImpersonation(
+ VOID
+ )
+{
+ HANDLE Handle;
+ NTSTATUS Status;
+
+ Handle = NULL;
+ Status = NtSetInformationThread(NtCurrentThread(), ThreadImpersonationToken,
+ (PVOID)&Handle, sizeof(HANDLE));
+ ASSERT(NT_SUCCESS(Status));
+}
+
+//
+// MakePosixId -- convert the given SID into a Posix Id. This basically
+// means find the Posix Offset and add it to the last sub-authority
+// in the SID. The Posix Id is returned.
+//
+
+uid_t
+MakePosixId(PSID Sid)
+{
+ NTSTATUS Status;
+ LSA_HANDLE
+ PolicyHandle,
+ TrustedDomainHandle;
+ PTRUSTED_POSIX_OFFSET_INFO
+ pPosixOff;
+ OBJECT_ATTRIBUTES
+ Obj;
+ SECURITY_QUALITY_OF_SERVICE
+ SecurityQoS;
+ CHAR buf[SECURITY_DESCRIPTOR_MIN_LENGTH];
+ PSECURITY_DESCRIPTOR
+ SecurityDescriptor = (PVOID)buf;
+ PSID DomainSid;
+ ULONG RelativeId, offset;
+ UNICODE_STRING
+ DCName,
+ Domain_U;
+ PPOLICY_ACCOUNT_DOMAIN_INFO
+ AccountDomainInfo;
+ PPOLICY_PRIMARY_DOMAIN_INFO
+ PrimaryDomainInfo;
+ UCHAR SubAuthCount;
+ LPBYTE netbuf;
+ ULONG i;
+
+ SubAuthCount = *RtlSubAuthorityCountSid(Sid);
+ RelativeId = *RtlSubAuthoritySid(Sid, SubAuthCount - 1);
+
+ //
+ // Map S-1-5-5-X-Y to Id 0xFFF
+ //
+
+ if (3 == SubAuthCount &&
+ 5 == RtlIdentifierAuthoritySid(Sid)->Value[5] &&
+ 5 == *RtlSubAuthoritySid(Sid, 0)) {
+ return 0xFFF;
+ }
+
+ //
+ // First copy the given Sid to a Sid for that domain.
+ //
+
+ DomainSid = RtlAllocateHeap(PsxHeap, 0, RtlLengthSid(Sid));
+ if (NULL == DomainSid) {
+ KdPrint(("PSXSS: MakePosixId: no memory\n"));
+ return 0;
+ }
+
+ Status = RtlCopySid(RtlLengthSid(Sid), DomainSid, Sid);
+ ASSERT(NT_SUCCESS(Status));
+
+ --*RtlSubAuthorityCountSid(DomainSid);
+
+ //
+ // See if the offset for the domain is already known.
+ //
+
+ if (INVALID_POSIX_OFFSET != (offset = GetOffsetBySid(DomainSid))) {
+ // XXX.mjb: close handles, free memory.
+ RtlFreeHeap(PsxHeap, 0, (PVOID)DomainSid);
+ return (offset | RelativeId);
+ }
+
+ //
+ // If the Domain part of the passed-in Sid is our account domain,
+ // then the offset is known.
+ //
+
+ SecurityQoS.ImpersonationLevel = SecurityIdentification;
+ SecurityQoS.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ SecurityQoS.EffectiveOnly = TRUE;
+
+ InitializeObjectAttributes(&Obj, NULL, 0, NULL, NULL);
+ Obj.SecurityQualityOfService = &SecurityQoS;
+
+ Status = LsaOpenPolicy(NULL, &Obj, GENERIC_EXECUTE, &PolicyHandle);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: Can't open policy: 0x%x\n", Status));
+ RtlFreeHeap(PsxHeap, 0, (PVOID)DomainSid);
+ return 0;
+ }
+
+ Status = LsaQueryInformationPolicy(PolicyHandle,
+ PolicyAccountDomainInformation, (PVOID *)&AccountDomainInfo);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: Can't query info policy: 0x%x\n", Status));
+ return 0;
+ }
+ ASSERT(NULL != AccountDomainInfo->DomainSid);
+
+ if (RtlEqualSid(AccountDomainInfo->DomainSid, DomainSid)) {
+ MapSidToOffset(DomainSid, SE_ACCOUNT_DOMAIN_POSIX_OFFSET);
+ LsaFreeMemory(AccountDomainInfo);
+ LsaClose(PolicyHandle);
+ return RelativeId | SE_ACCOUNT_DOMAIN_POSIX_OFFSET;
+ }
+ LsaFreeMemory(AccountDomainInfo);
+
+ Status = LsaQueryInformationPolicy(PolicyHandle,
+ PolicyPrimaryDomainInformation, (PVOID *)&PrimaryDomainInfo);
+ ASSERT(NT_SUCCESS(Status));
+
+ if (NULL == PrimaryDomainInfo->Sid) {
+ //
+ // This machine does not have a primary domain, and the
+ // sid we're mapping does not belong to the account domain
+ // and is not a well-known sid.
+ //
+ RtlFreeHeap(PsxHeap, 0, (PVOID)DomainSid);
+ LsaFreeMemory(PrimaryDomainInfo);
+ LsaClose(PolicyHandle);
+ return RelativeId;
+ }
+
+ if (NULL != PrimaryDomainInfo->Sid &&
+ RtlEqualSid(PrimaryDomainInfo->Sid, DomainSid)) {
+ MapSidToOffset(DomainSid, SE_PRIMARY_DOMAIN_POSIX_OFFSET);
+ LsaFreeMemory(PrimaryDomainInfo);
+ LsaClose(PolicyHandle);
+ return RelativeId | SE_PRIMARY_DOMAIN_POSIX_OFFSET;
+ }
+
+ Status = NetGetAnyDCName(NULL,
+ PrimaryDomainInfo->Name.Buffer,
+ &netbuf);
+ if (Status != ERROR_SUCCESS) {
+ RtlFreeHeap(PsxHeap, 0, (PVOID)DomainSid);
+ LsaFreeMemory(PrimaryDomainInfo);
+ LsaClose(PolicyHandle);
+ return RelativeId;
+ }
+ DCName.Buffer = (PVOID)netbuf;
+ NetApiBufferSize(netbuf, (LPDWORD)&DCName.MaximumLength);
+ DCName.Length = wcslen((PWCHAR)netbuf) * sizeof(WCHAR);
+
+ LsaClose(PolicyHandle);
+
+ Status = LsaOpenPolicy(&DCName, &Obj, GENERIC_EXECUTE, &PolicyHandle);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: Can't open policy on DC %wZ: 0x%x\n", &DCName,
+ Status));
+ LsaFreeMemory(PrimaryDomainInfo);
+ RtlFreeHeap(PsxHeap, 0, (PVOID)DomainSid);
+ return RelativeId;
+ }
+
+ NetApiBufferFree(netbuf);
+ LsaFreeMemory(PrimaryDomainInfo);
+
+ Status = LsaOpenTrustedDomain(PolicyHandle, DomainSid, GENERIC_EXECUTE,
+ &TrustedDomainHandle);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: Can't open trusted domain\n"));
+ RtlFreeHeap(PsxHeap, 0, (PVOID)DomainSid);
+ return RelativeId;
+ }
+
+ Status = LsaQueryInfoTrustedDomain(TrustedDomainHandle,
+ TrustedPosixOffsetInformation,
+ (PVOID)&pPosixOff);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: Can't query Posix offset info: 0x%x\n",
+ Status));
+ LsaClose(PolicyHandle);
+ LsaClose(TrustedDomainHandle);
+ RtlFreeHeap(PsxHeap, 0, (PVOID)DomainSid);
+ return RelativeId;
+ }
+
+ offset = pPosixOff->Offset;
+
+ LsaFreeMemory(pPosixOff);
+
+ if (offset & 0xFFFF) {
+ KdPrint(("PSXSS: bad PsxOffset 0x%x\n", offset));
+ RtlFreeHeap(PsxHeap, 0, (PVOID)DomainSid);
+ LsaClose(TrustedDomainHandle);
+ LsaClose(PolicyHandle);
+ offset = 0;
+ }
+
+ ASSERT(INVALID_POSIX_OFFSET != offset);
+
+ MapSidToOffset(DomainSid, offset);
+
+ //
+ // Do not free DomainSid -- there is still a reference to it in
+ // the Sid-Offset cache (put there by MapSidToOffset).
+ //
+
+ LsaClose(PolicyHandle);
+ LsaClose(TrustedDomainHandle);
+
+ return offset | RelativeId;
+}
+
+typedef struct _SID_AND_OFFSET {
+ LIST_ENTRY Links;
+ PSID Sid;
+ ULONG Offset;
+} SID_AND_OFFSET, *PSID_AND_OFFSET;
+
+LIST_ENTRY SidList;
+RTL_CRITICAL_SECTION SidListMutex;
+
+//
+// GetOffsetBySid -- search the SidList for the given Sid. If we've
+// encountered this domain before, we'll know the Posix offset,
+// which is returned. If not, INVALID_POSIX_OFFSET is returned.
+//
+ULONG
+GetOffsetBySid(PSID Sid)
+{
+ PSID_AND_OFFSET pSO;
+ ULONG Offset = INVALID_POSIX_OFFSET;
+
+ RtlEnterCriticalSection(&SidListMutex);
+
+ for (pSO = (PSID_AND_OFFSET)SidList.Flink;
+ pSO != (PSID_AND_OFFSET)&SidList;
+ pSO = (PSID_AND_OFFSET)pSO->Links.Flink) {
+ if (RtlEqualSid(Sid, pSO->Sid)) {
+ Offset = pSO->Offset;
+ break;
+ }
+ }
+
+ RtlLeaveCriticalSection(&SidListMutex);
+
+ return Offset;
+}
+
+//
+// GetSidByOffset -- search the SidList for the given offset. Called in
+// the process of converting a uid or gid to a Sid, as in getpwuid().
+//
+PSID
+GetSidByOffset(ULONG Offset)
+{
+ PSID_AND_OFFSET pSO;
+ PSID Sid = NULL;
+
+ RtlEnterCriticalSection(&SidListMutex);
+
+ for (pSO = (PSID_AND_OFFSET)SidList.Flink;
+ pSO != (PSID_AND_OFFSET)&SidList;
+ pSO = (PSID_AND_OFFSET)pSO->Links.Flink) {
+ if (Offset == pSO->Offset) {
+ Sid = pSO->Sid;
+ break;
+ }
+ }
+
+ RtlLeaveCriticalSection(&SidListMutex);
+
+ return Sid;
+}
+
+//
+// MapSidToOffset -- add the given sid and offset to the cache. If there's
+// an error, like no memory, it's simply dropped on the floor.
+//
+VOID
+MapSidToOffset(PSID Sid, ULONG Offset)
+{
+ PSID_AND_OFFSET pSO;
+
+ pSO = RtlAllocateHeap(PsxHeap, 0, sizeof(*pSO));
+ if (NULL == pSO) {
+ return;
+ }
+
+ pSO->Sid = Sid;
+ pSO->Offset = Offset;
+
+ RtlEnterCriticalSection(&SidListMutex);
+
+ InsertHeadList(&SidList, &pSO->Links);
+
+ RtlLeaveCriticalSection(&SidListMutex);
+}
+
+//
+// InitSidList -- do initialization, including mapping special Sids to
+// their appropriate offsets. No locking is done, assumed to be
+// called in a single-threaded way.
+//
+VOID
+InitSidList(VOID)
+{
+ NTSTATUS Status;
+ PSID Sid;
+ SID_IDENTIFIER_AUTHORITY
+ AuthSec = SECURITY_NT_AUTHORITY,
+ Auth0 = SECURITY_NULL_SID_AUTHORITY,
+ Auth1 = SECURITY_WORLD_SID_AUTHORITY,
+ Auth2 = SECURITY_LOCAL_SID_AUTHORITY,
+ Auth3 = SECURITY_CREATOR_SID_AUTHORITY,
+ Auth4 = SECURITY_NON_UNIQUE_AUTHORITY,
+ Auth5 = SECURITY_NT_AUTHORITY;
+
+ RtlInitializeCriticalSection(&SidListMutex);
+ InitializeListHead(&SidList);
+
+ Status = RtlAllocateAndInitializeSid(&Auth0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, &Sid);
+ ASSERT(NT_SUCCESS(Status));
+ MapSidToOffset(Sid, SE_NULL_POSIX_ID);
+
+ Status = RtlAllocateAndInitializeSid(&Auth1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, &Sid);
+ ASSERT(NT_SUCCESS(Status));
+ MapSidToOffset(Sid, SE_WORLD_POSIX_ID);
+
+ Status = RtlAllocateAndInitializeSid(&Auth2, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, &Sid);
+ ASSERT(NT_SUCCESS(Status));
+ MapSidToOffset(Sid, SE_LOCAL_POSIX_ID);
+
+ Status = RtlAllocateAndInitializeSid(&Auth3, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, &Sid);
+ ASSERT(NT_SUCCESS(Status));
+ MapSidToOffset(Sid, SE_CREATOR_OWNER_POSIX_ID);
+
+ Status = RtlAllocateAndInitializeSid(&Auth4, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, &Sid);
+ ASSERT(NT_SUCCESS(Status));
+ MapSidToOffset(Sid, SE_NON_UNIQUE_POSIX_ID);
+
+ Status = RtlAllocateAndInitializeSid(&Auth5, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, &Sid);
+ ASSERT(NT_SUCCESS(Status));
+ MapSidToOffset(Sid, SE_AUTHORITY_POSIX_ID);
+
+ //
+ // "Builtin" domain has known offset.
+ //
+
+ Status = RtlAllocateAndInitializeSid(&AuthSec, 1,
+ SECURITY_BUILTIN_DOMAIN_RID, 0, 0, 0, 0, 0, 0, 0, &Sid);
+ ASSERT(NT_SUCCESS(Status));
+ MapSidToOffset(Sid, SE_BUILT_IN_DOMAIN_POSIX_OFFSET);
+}
+
+//
+// AccessMaskToMode -- convert a set of NT ACCESS_MASKS to the POSIX
+// mode_t.
+//
+mode_t
+AccessMaskToMode(
+ ACCESS_MASK UserAccess,
+ ACCESS_MASK GroupAccess,
+ ACCESS_MASK OtherAccess
+ )
+{
+ mode_t Mode = 0;
+ int i;
+ PACCESS_MASK pAM;
+
+ //
+ // Make sure that if a GENERIC_ACCESS is set, we notice that
+ // the mask implies FILE_GENERIC_ACCESS.
+ //
+
+ for (i = 0; i < 3; ++i) {
+ switch (i) {
+ case 0:
+ pAM = &UserAccess;
+ break;
+ case 1:
+ pAM = &GroupAccess;
+ break;
+ case 2:
+ pAM = &OtherAccess;
+ break;
+ }
+ if (*pAM & GENERIC_READ) {
+ *pAM |= FILE_GENERIC_READ;
+ }
+ if (*pAM & GENERIC_WRITE) {
+ *pAM |= FILE_GENERIC_WRITE;
+ }
+ if (*pAM & GENERIC_EXECUTE) {
+ *pAM |= FILE_GENERIC_EXECUTE;
+ }
+ if (*pAM & GENERIC_ALL) {
+ *pAM |= FILE_ALL_ACCESS;
+ }
+ }
+
+
+ if (UserAccess & FILE_READ_DATA) {
+ Mode |= S_IRUSR;
+ }
+ if ((UserAccess & FILE_WRITE_DATA) &&
+ (UserAccess & FILE_APPEND_DATA)) {
+ Mode |= S_IWUSR;
+ }
+ if (UserAccess & FILE_EXECUTE) {
+ Mode |= S_IXUSR;
+ }
+
+ if (GroupAccess & FILE_READ_DATA) {
+ Mode |= S_IRGRP;
+ }
+ if ((GroupAccess & FILE_WRITE_DATA) &&
+ (GroupAccess & FILE_APPEND_DATA)) {
+ Mode |= S_IWGRP;
+ }
+ if (GroupAccess & FILE_EXECUTE) {
+ Mode |= S_IXGRP;
+ }
+
+ if (OtherAccess & FILE_READ_DATA) {
+ Mode |= S_IROTH;
+ }
+ if ((OtherAccess & FILE_WRITE_DATA) &&
+ (OtherAccess & FILE_APPEND_DATA)) {
+ Mode |= S_IWOTH;
+ }
+ if (OtherAccess & FILE_EXECUTE) {
+ Mode |= S_IXOTH;
+ }
+ return Mode;
+}
+
+void
+ModeToAccessMask(
+ mode_t Mode,
+ PACCESS_MASK pUserAccess,
+ PACCESS_MASK pGroupAccess,
+ PACCESS_MASK pOtherAccess
+ )
+{
+ //
+ // All ACL's have these standard permissions:
+ // READ_ATTR and READ_EA so anybody can access() any file.
+ //
+
+ *pUserAccess = SYNCHRONIZE |
+ READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA;
+ *pGroupAccess = *pOtherAccess = *pUserAccess;
+
+ //
+ // The owner always gets WRITE_DAC (for chmod), FILE_WRITE_ATTR
+ // (for utimes), and WRITE_OWNER (for chgrp).
+ //
+ *pUserAccess |= (WRITE_DAC | WRITE_OWNER | FILE_WRITE_ATTRIBUTES);
+
+ if (Mode & S_IRUSR) {
+ *pUserAccess |= FILE_GENERIC_READ | FILE_LIST_DIRECTORY;
+ }
+ if (Mode & S_IWUSR) {
+ *pUserAccess |= FILE_GENERIC_WRITE | FILE_DELETE_CHILD;
+ }
+ if (Mode & S_IXUSR) {
+ *pUserAccess |= FILE_GENERIC_EXECUTE;
+ }
+
+ if (Mode & S_IRGRP) {
+ *pGroupAccess |= FILE_GENERIC_READ | FILE_LIST_DIRECTORY;
+ }
+ if (Mode & S_IWGRP) {
+ *pGroupAccess |= FILE_GENERIC_WRITE | FILE_DELETE_CHILD;
+ }
+ if (Mode & S_IXGRP) {
+ *pGroupAccess |= FILE_GENERIC_EXECUTE;
+ }
+
+ if (Mode & S_IROTH) {
+ *pOtherAccess |= FILE_GENERIC_READ | FILE_LIST_DIRECTORY;
+ }
+ if (Mode & S_IWOTH) {
+ *pOtherAccess |= FILE_GENERIC_WRITE | FILE_DELETE_CHILD;
+ }
+ if (Mode & S_IXOTH) {
+ *pOtherAccess |= FILE_GENERIC_EXECUTE;
+ }
+}
diff --git a/private/posix/psxss/sbapi.c b/private/posix/psxss/sbapi.c
new file mode 100644
index 000000000..31432aa5a
--- /dev/null
+++ b/private/posix/psxss/sbapi.c
@@ -0,0 +1,83 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sbapi.c
+
+Abstract:
+
+ This module contains the implementations of the Sb API calls exported
+ by the POSIX Emulation SubSystem to the Session Manager SubSystem.
+
+Author:
+
+ Steve Wood (stevewo) 26-Sep-1989
+
+Revision History:
+
+ Ellen Aycock-Wright (ellena) 15-Jul-91 Modified for POSIX
+--*/
+
+#include "psxsrv.h"
+
+#if 0
+// XXX.mjb: Don't believe this routine is ever referenced or called
+// (waiting on the day when Posix sessions are started by the NT session
+// manager?), and at the moment it doesn't compile very well. Simple
+// solution...
+
+BOOLEAN
+PsxSbCreateSession(
+ IN PSBAPIMSG Msg
+ )
+{
+ PSBCREATESESSION a = &Msg->u.CreateSession;
+ PPSX_PROCESS Process;
+ NTSTATUS Status;
+
+ Process = PsxAllocateProcess(&a->ProcessInformation.ClientId);
+
+ if (Process == NULL) {
+ Msg->ReturnedStatus = STATUS_NO_MEMORY;
+ return TRUE;
+ }
+
+ PsxInitializeProcess(Process, NULL, a->SessionId,
+ a->ProcessInformation.Process,
+ a->ProcessInformation.Thread, NULL);
+
+ //
+ // Setup the initial directory prefix stuff
+ //
+
+ PsxInitializeDirectories(Process);
+
+ Msg->ReturnedStatus = NtResumeThread(a->ProcessInformation.Thread, NULL);
+
+ return TRUE;
+}
+#endif
+
+BOOLEAN
+PsxSbTerminateSession(
+ IN PSBAPIMSG Msg
+ )
+{
+ PSBTERMINATESESSION a = &Msg->u.TerminateSession;
+
+ Msg->ReturnedStatus = STATUS_NOT_IMPLEMENTED;
+ return( TRUE );
+}
+
+BOOLEAN
+PsxSbForeignSessionComplete(
+ IN PSBAPIMSG Msg
+ )
+{
+ PSBFOREIGNSESSIONCOMPLETE a = &Msg->u.ForeignSessionComplete;
+
+ Msg->ReturnedStatus = STATUS_NOT_IMPLEMENTED;
+ return( TRUE );
+}
diff --git a/private/posix/psxss/sbinit.c b/private/posix/psxss/sbinit.c
new file mode 100644
index 000000000..85c0be99f
--- /dev/null
+++ b/private/posix/psxss/sbinit.c
@@ -0,0 +1,82 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ sbinit.c
+
+Abstract:
+
+ This module contains code to initialize the SbApiPort of the
+ POSIX Subsystem.
+
+Author:
+ Steve Wood (stevewo) 22-Aug-1989
+
+Revision History:
+
+ Ellen Aycock-Wright (ellena) 10-Jul-1991 Modified for POSIX
+
+--*/
+
+#include "psxsrv.h"
+#include <windef.h>
+#include <winbase.h>
+
+NTSTATUS
+PsxSbApiPortInitialize( VOID )
+{
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
+ RtlInitUnicodeString(&PsxSbApiPortName_U, PSX_SS_SBAPI_PORT_NAME);
+
+ IF_PSX_DEBUG( LPC ) {
+ KdPrint(("PSXSS: Creating %wZ port and associated thread\n",
+ &PsxSbApiPortName_U));
+ }
+
+ InitializeObjectAttributes(&ObjectAttributes, &PsxSbApiPortName_U, 0, NULL,
+ NULL);
+ Status = NtCreatePort(&PsxSbApiPort, &ObjectAttributes,
+ sizeof(SBCONNECTINFO), sizeof(SBAPIMSG),
+ sizeof(SBAPIMSG) * 32);
+
+ ASSERT(NT_SUCCESS(Status));
+ if (!NT_SUCCESS(Status)) {
+ NtTerminateProcess(NtCurrentProcess(), 1);
+ }
+
+ PsxServerThreadHandles[PSX_SS_SBAPI_REQUEST_THREAD] = CreateThread(NULL, 0,
+ (LPTHREAD_START_ROUTINE)PsxSbApiRequestThread, NULL,
+ CREATE_SUSPENDED,
+ (LPDWORD)&PsxServerThreadClientIds[PSX_SS_SBAPI_REQUEST_THREAD]);
+
+ ASSERT(NULL != PsxServerThreadHandles[PSX_SS_SBAPI_REQUEST_THREAD]);
+ if (NULL == PsxServerThreadHandles[PSX_SS_SBAPI_REQUEST_THREAD]) {
+ NtTerminateProcess(NtCurrentProcess(), 1);
+ }
+
+ Status = ResumeThread(PsxServerThreadHandles[PSX_SS_SBAPI_REQUEST_THREAD]);
+
+ ASSERT(-1 != Status);
+
+ return Status;
+}
+
+VOID
+PsxSbApiPortTerminate(
+ NTSTATUS Status
+ )
+{
+ IF_PSX_DEBUG(LPC) {
+ KdPrint(("PSXSS: Closing %Z port and associated thread\n",
+ &PsxSbApiPortName));
+ }
+ NtTerminateThread(PsxServerThreadHandles[PSX_SS_SBAPI_REQUEST_THREAD],
+ Status);
+
+ NtClose(PsxSbApiPort);
+ NtClose(PsxSmApiPort);
+}
diff --git a/private/posix/psxss/sbreqst.c b/private/posix/psxss/sbreqst.c
new file mode 100644
index 000000000..27e1fda04
--- /dev/null
+++ b/private/posix/psxss/sbreqst.c
@@ -0,0 +1,143 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sbreqst.c
+
+Abstract:
+
+ This module contains the Server Request thread procedure for the Sb
+ API calls exported by the POSIX Emulation SubSystem to the Session
+ Manager SubSystem.
+
+Author:
+
+ Steve Wood (stevewo) 20-Sep-1989
+
+Revision History:
+
+ Ellen Aycock-Wright (ellena) 16-Jul-91 Modified for POSIX
+
+--*/
+
+#include "psxsrv.h"
+
+NTSTATUS
+PsxSbApiHandleConnectionRequest(
+ IN PSBAPIMSG Message
+ );
+
+//PSB_API_ROUTINE PsxServerSbApiDispatch[ SbMaxApiNumber+1 ] = {
+// PsxSbCreateSession,
+// PsxSbTerminateSession,
+// PsxSbForeignSessionComplete,
+// NULL
+//};
+
+NTSTATUS
+PsxSbApiRequestThread(
+ IN PVOID Parameter
+ )
+{
+ NTSTATUS Status;
+ SBAPIMSG ReceiveMsg;
+ PSBAPIMSG ReplyMsg;
+
+ UNREFERENCED_PARAMETER(Parameter);
+
+ ReplyMsg = NULL;
+ while (TRUE) {
+ IF_PSX_DEBUG( LPC ) {
+ KdPrint(("PSXSS: Sb Api Request Thread waiting...\n"));
+ }
+ Status = NtReplyWaitReceivePort(PsxSbApiPort, NULL,
+ (PPORT_MESSAGE)ReplyMsg, (PPORT_MESSAGE)&ReceiveMsg);
+ if (Status != 0) {
+ if (NT_SUCCESS(Status)) {
+ continue; // Try again if alerted or a failure
+ } else {
+ IF_PSX_DEBUG( LPC ) {
+ KdPrint(("PSXSS: SB ReceivePort failed:Status == %X\n",
+ Status));
+ }
+ break;
+ }
+ }
+
+ //
+ // Check to see if this is a connection request and handle
+ //
+
+ if (ReceiveMsg.h.u2.s2.Type == LPC_CONNECTION_REQUEST) {
+ PsxSbApiHandleConnectionRequest( &ReceiveMsg );
+ ReplyMsg = NULL;
+ continue;
+ }
+
+ if (ReceiveMsg.ApiNumber >= SbMaxApiNumber) {
+ IF_PSX_DEBUG(LPC) {
+ KdPrint(("PSXSS: %lx is invalid Sb ApiNumber\n",
+ ReceiveMsg.ApiNumber));
+ }
+
+ ReceiveMsg.ApiNumber = SbMaxApiNumber;
+ }
+
+ ReplyMsg = &ReceiveMsg;
+ if (ReceiveMsg.ApiNumber < SbMaxApiNumber) {
+// if (!(*PsxServerSbApiDispatch[ ReceiveMsg.ApiNumber ])( &ReceiveMsg )) {
+ ReplyMsg = NULL;
+// }
+// }
+// else {
+// ReplyMsg->ReturnedStatus = STATUS_NOT_IMPLEMENTED;
+ }
+ }
+
+ NtTerminateThread( NtCurrentThread(), Status );
+ //
+ // This line should never be executed
+ //
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+PsxSbApiHandleConnectionRequest(
+ IN PSBAPIMSG Message
+ )
+{
+ NTSTATUS st;
+ REMOTE_PORT_VIEW ClientView;
+ HANDLE CommunicationPort;
+
+ //
+ // The protocol for a subsystem is to connect to the session manager,
+ // then to listen and accept a connection from the session manager
+ //
+
+ ClientView.Length = sizeof(ClientView);
+ st = NtAcceptConnectPort(
+ &CommunicationPort,
+ NULL,
+ (PPORT_MESSAGE)Message,
+ TRUE,
+ NULL,
+ &ClientView
+ );
+
+ if ( !NT_SUCCESS(st) ) {
+ KdPrint(("PSXSS: Sb Accept Connection failed %lx\n",st));
+ return st;
+ }
+
+ st = NtCompleteConnectPort(CommunicationPort);
+
+ if ( !NT_SUCCESS(st) ) {
+ KdPrint(("PSXSS: Sb Complete Connection failed %lx\n",st));
+ }
+
+ return st;
+}
diff --git a/private/posix/psxss/session.c b/private/posix/psxss/session.c
new file mode 100644
index 000000000..6c9dea53a
--- /dev/null
+++ b/private/posix/psxss/session.c
@@ -0,0 +1,115 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ session.c
+
+Abstract:
+
+ This module contains the worker routines called by the Sb API Request
+ routines.
+
+Author:
+
+ Steve Wood (stevewo) 04-Oct-1989
+
+Revision History:
+ 15-Jul-91: Modified for POSIX subsystem.
+
+--*/
+
+
+#include "psxsrv.h"
+
+
+NTSTATUS
+PsxInitializeNtSessionList( VOID )
+{
+ NTSTATUS Status;
+
+ Status = RtlInitializeCriticalSection( &PsxNtSessionLock );
+ return( Status );
+}
+
+RTL_CRITICAL_SECTION ConnectingTerminalListMutex;
+LIST_ENTRY ConnectingTerminalList;
+
+
+NTSTATUS
+InitConnectingTerminalList(
+ VOID
+ )
+{
+ NTSTATUS Status;
+
+ InitializeListHead(&ConnectingTerminalList);
+ Status = RtlInitializeCriticalSection(&ConnectingTerminalListMutex);
+ return Status;
+}
+
+//
+// AddConnectingTerminal - a new terminal has connected to the posix
+// subsystem, but has not yet asked to have a process created to
+// be the session leader. We keep track of the information about
+// the terminal in the ConnectingTerminalList until we have a process
+// structure to put it in.
+//
+NTSTATUS
+AddConnectingTerminal(
+ int Id,
+ HANDLE CommPort,
+ HANDLE ReqPort
+ )
+{
+ PPSX_CONTROLLING_TTY Terminal;
+
+ Terminal = RtlAllocateHeap(PsxHeap, 0, sizeof(*Terminal));
+ if (NULL == Terminal) {
+ return STATUS_NO_MEMORY;
+ }
+
+ Terminal->ReferenceCount = 1;
+ Terminal->UniqueId = Id;
+ Terminal->ConsoleCommPort = CommPort;
+ Terminal->ConsolePort = ReqPort;
+ RtlInitializeCriticalSection(&Terminal->Lock);
+
+ RtlEnterCriticalSection(&ConnectingTerminalListMutex);
+
+ InsertHeadList(&ConnectingTerminalList, &Terminal->Links);
+
+ RtlLeaveCriticalSection(&ConnectingTerminalListMutex);
+}
+
+//
+// GetConnectingTerminal - the terminal with the given id has requested
+// a process for session leader, so remove it from the list and return
+// it. NULL is returned if the terminal is not on the list.
+//
+PPSX_CONTROLLING_TTY
+GetConnectingTerminal(
+ int Id
+ )
+{
+ PPSX_CONTROLLING_TTY Terminal;
+
+ RtlEnterCriticalSection(&ConnectingTerminalListMutex);
+
+ for (Terminal = (PVOID)ConnectingTerminalList.Flink;
+ Terminal != (PVOID)&ConnectingTerminalList;
+ Terminal = (PVOID)Terminal->Links.Flink) {
+ if ((unsigned int)Id == Terminal->UniqueId) {
+ RemoveEntryList(&Terminal->Links);
+ Terminal->Links.Flink = Terminal->Links.Blink = NULL;
+ RtlLeaveCriticalSection(&ConnectingTerminalListMutex);
+
+ return Terminal;
+ }
+ }
+
+ RtlLeaveCriticalSection(&ConnectingTerminalListMutex);
+ return NULL;
+
+}
diff --git a/private/posix/psxss/sigapi.c b/private/posix/psxss/sigapi.c
new file mode 100644
index 000000000..81546d8cb
--- /dev/null
+++ b/private/posix/psxss/sigapi.c
@@ -0,0 +1,429 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sigapi.c
+
+Abstract:
+
+ This module implements all signal oriented APIs.
+
+Author:
+
+ Mark Lucovsky (markl) 30-Mar-1989
+
+Revision History:
+
+--*/
+
+
+#include "psxsrv.h"
+
+
+BOOLEAN
+PsxSigAction (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements POSIX sigaction
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the open request.
+
+Return Value:
+
+ TRUE - The contens of *m should be used to generate a reply.
+
+--*/
+
+{
+ PPSX_SIGACTION_MSG args;
+
+ args = &m->u.SigAction;
+
+ //
+ // Validate Signal
+ //
+
+ if ( !ISSIGNOINRANGE(args->Sig) || args->Sig <= 0 ) {
+ m->Error = EINVAL;
+ return TRUE;
+ }
+
+ if (ARGUMENT_PRESENT(args->ActSpecified) ) {
+
+ //
+ // Since call is attempting to set a signal action, fail if:
+ // attemping to catch a signal that cant be caught
+ // SIGKILL, SIGSTOP
+ // attemping to ignore a signal that cant be ignored
+ // SIGKILL, SIGSTOP
+ //
+ // SIGKILL and SIGSTOP can only be set to SIG_DFL !
+ //
+
+ if ( ((args->Sig == SIGKILL) || (args->Sig == SIGSTOP)) &&
+ (args->Act.sa_handler != (_handler) SIG_DFL) ) {
+
+ m->Error = EINVAL;
+
+ return TRUE;
+
+ } else {
+
+ //
+ // Clear SIGKILL and SIGSTOP from the new block
+ // mask without causing an error
+ //
+
+ SIGDELSET(&args->Act.sa_mask, SIGKILL);
+ SIGDELSET(&args->Act.sa_mask, SIGSTOP);
+ args->Act.sa_mask &= _SIGFULLSET;
+
+ AcquireProcessLock(p);
+
+ args->Oact = p->SignalDataBase.SignalDisposition[args->Sig-1];
+ p->SignalDataBase.SignalDisposition[args->Sig-1] = args->Act;
+
+ if (SIGISMEMBER(&p->SignalDataBase.PendingSignalMask, args->Sig) ) {
+
+ //
+ // Signal whose action is being changed is pending.
+ //
+ // If signal iction is being set to ignored, or if being set to
+ // default action, and default action is to ignore the signal,
+ // then clear the signal from the set of pending signals.
+ //
+
+ if ( (args->Act.sa_handler == SIG_IGN) ||
+ ((args->Act.sa_handler == SIG_DFL) && (args->Sig == SIGCHLD)) ) {
+
+ SIGDELSET(&p->SignalDataBase.PendingSignalMask, args->Sig);
+
+ }
+ }
+
+ ReleaseProcessLock(p);
+ }
+ } else {
+
+ AcquireProcessLock(p);
+
+ args->Oact = p->SignalDataBase.SignalDisposition[args->Sig-1];
+
+ ReleaseProcessLock(p);
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+PsxSigProcMask(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements POSIX sigaction
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the open request.
+
+Return Value:
+
+ TRUE - The contens of *m should be used to generate a reply.
+
+--*/
+
+{
+ PPSX_SIGPROCMASK_MSG args;
+
+ args = &m->u.SigProcMask;
+
+ AcquireProcessLock(p);
+
+ args->Oset = p->SignalDataBase.BlockedSignalMask;
+
+ if ( ARGUMENT_PRESENT(args->SetSpecified) ) {
+
+ switch (args->How) {
+
+ case SIG_BLOCK:
+
+ p->SignalDataBase.BlockedSignalMask |= args->Set;
+ break;
+
+ case SIG_UNBLOCK:
+
+ p->SignalDataBase.BlockedSignalMask &= ~args->Set;
+ break;
+
+ case SIG_SETMASK:
+
+ p->SignalDataBase.BlockedSignalMask = args->Set;
+ break;
+
+ default:
+ m->Error = EINVAL;
+ ReleaseProcessLock(p);
+ return TRUE;
+ }
+ SIGDELSET(&p->SignalDataBase.BlockedSignalMask,SIGKILL);
+ SIGDELSET(&p->SignalDataBase.BlockedSignalMask,SIGSTOP);
+ }
+
+ ReleaseProcessLock(p);
+
+ return TRUE;
+}
+
+BOOLEAN
+PsxSigPending(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+{
+ PPSX_SIGPENDING_MSG args;
+
+ args = &m->u.SigPending;
+
+ args->Set = p->SignalDataBase.PendingSignalMask;
+ m->Error = 0;
+
+ return TRUE;
+}
+
+BOOLEAN
+PsxSigSuspend(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements POSIX sigsuspend
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the request.
+
+Return Value:
+
+ FALSE - A reply will not be generated until a signal is generated that
+ either terminates the process or is delivered to the process.
+
+--*/
+
+{
+ PPSX_SIGSUSPEND_MSG args;
+ sigset_t NewBlockMask;
+ NTSTATUS Status;
+
+ args = &m->u.SigSuspend;
+
+ AcquireProcessLock(p);
+
+ NewBlockMask = p->SignalDataBase.BlockedSignalMask;
+
+ //
+ // For sigsuspend, sigmask is specified; otherwise, the current
+ // blocked signal mask is used
+ //
+
+ if ( args->SigMaskSpecified ) {
+ p->SignalDataBase.BlockedSignalMask = args->SigMask;
+ }
+
+ SIGDELSET(&p->SignalDataBase.BlockedSignalMask,SIGKILL);
+ SIGDELSET(&p->SignalDataBase.BlockedSignalMask,SIGSTOP);
+
+ ReleaseProcessLock(p);
+
+ Status = BlockProcess(p, (PVOID)NewBlockMask, PsxSigSuspendHandler,
+ m, FALSE, NULL);
+ if (!NT_SUCCESS(Status)) {
+ m->Error = PsxStatusToErrno(Status);
+ return TRUE;
+ }
+
+ //
+ // The process was blocked -- don't reply to the api request yet.
+ //
+ return FALSE;
+
+}
+
+
+BOOLEAN
+PsxNull (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m
+ )
+
+/*++
+
+Routine Description:
+
+ The null system service. This service is used to get a process to
+ look at its pending signals.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the request.
+
+Return Value:
+
+ TRUE - The contens of *m should be used to generate a reply.
+
+--*/
+
+{
+ //
+ // The process may fork again.
+ //
+
+ p->Flags &= ~P_NO_FORK;
+ return TRUE;
+}
+
+BOOLEAN
+PsxKill(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+
+/*++
+
+Routine Description:
+
+ This function implements posix kill().
+
+Arguments:
+
+ p - Supplies the address of the calling process
+
+ m - Supplies the address of the related message
+
+Return Value:
+
+ TRUE - Always succeeds and generates a reply
+
+--*/
+{
+ PPSX_KILL_MSG args;
+ PPSX_PROCESS cp;
+ BOOLEAN SignalSent;
+ pid_t TargetGroup;
+
+ args = &m->u.Kill;
+
+ if ( ! ISSIGNOINRANGE(args->Sig) ) {
+ m->Error = EINVAL;
+ return TRUE;
+ }
+
+ if ( args->Pid < 0 && args->Pid != -1 ) {
+
+ TargetGroup = args->Pid * -1;
+ } else {
+ TargetGroup = 0;
+ }
+
+ SignalSent = FALSE;
+
+ //
+ // Lock the process table so that we can scan process table.
+ //
+
+ AcquireProcessStructureLock();
+
+ //
+ // Scan process table looking for pid
+ //
+
+ for (cp = FirstProcess; cp < LastProcess; cp++) {
+
+ //
+ // Only look at non-free slots
+ //
+
+ if (cp->Flags & P_FREE) {
+ continue;
+ }
+
+ if (args->Pid > 0) {
+
+ //
+ // Send signal to process whose Pid is args->Pid
+ //
+
+ if (cp->Pid == args->Pid) {
+ PsxSignalProcess(cp, args->Sig);
+ SignalSent = TRUE;
+ }
+ }
+ if (args->Pid == 0) {
+
+ //
+ // Send signal to all processes whose process group id
+ // matches the caller
+ //
+
+ if (cp->ProcessGroupId == p->ProcessGroupId) {
+ PsxSignalProcess(cp, args->Sig);
+ SignalSent = TRUE;
+ }
+ }
+ if (args->Pid == -1) {
+
+ //
+ // Posix does not define this.
+ //
+
+ continue;
+ }
+ if (TargetGroup) {
+
+ //
+ // Send signal to all processes whose process group id
+ // matches the absolute value of args->Pid
+ //
+
+ if ( cp->ProcessGroupId == TargetGroup ) {
+ PsxSignalProcess(cp, args->Sig);
+ SignalSent = TRUE;
+ }
+ }
+ }
+
+ if (!SignalSent) {
+ m->Error = ESRCH;
+ }
+
+ ReleaseProcessStructureLock();
+
+ return TRUE;
+}
diff --git a/private/posix/psxss/sigsup.c b/private/posix/psxss/sigsup.c
new file mode 100644
index 000000000..3119c3406
--- /dev/null
+++ b/private/posix/psxss/sigsup.c
@@ -0,0 +1,493 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sigsup.c
+
+Abstract:
+
+ This provides support routines to implement signal delivery and dispatch.
+
+Author:
+
+ Mark Lucovsky (markl) 30-Mar-1989
+
+Revision History:
+
+--*/
+
+
+#include "psxsrv.h"
+#define PENDING_SIGKILL (1<<SIGKILL)
+
+BOOLEAN
+PendingSignalHandledInside(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m,
+ IN sigset_t *RestoreBlockSigset OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure is called on system exit prior to reply generation. Its
+ purpose is to test for the presence of pending signals, and if found, to
+ process the signal.
+
+Arguments:
+
+ p - Supplies the address of the process to be checked.
+
+ m - Supplies the address of the process' current message.
+
+ RestoreBlockSigset - Supplies an optional blocked signal mask that
+ should be restored after the signal completes
+
+ Locks - Process lock is released upon return.
+
+
+Return Value:
+
+ TRUE - A pending signal was discovered that required extra time in the
+ system, or the signal action caused the process to terminate. The
+ process will be unlocked by this call, and no reply should be
+ generated.
+
+ FALSE - Either no pending signals were found, or a pending signal was
+ processed. The contents of *m should be used to generate a reply.
+
+--*/
+
+{
+ sigset_t Pending;
+ ULONG Signal;
+ ULONG SigAsMask;
+
+ AcquireProcessLock(p);
+
+ Pending = p->SignalDataBase.PendingSignalMask &
+ ~p->SignalDataBase.BlockedSignalMask;
+
+ if (!Pending) {
+ //
+ // Fast Path. Not pending, unblocked signals
+ //
+
+ ReleaseProcessLock(p);
+ return FALSE;
+ }
+
+ //
+ // Pending, Non-Blocked signals discovered
+ //
+
+ for (Signal = 1, SigAsMask = 1;
+ Signal <= _SIGMAXSIGNO;
+ Signal++, SigAsMask <<= 1L) {
+
+ if (Pending & SigAsMask) {
+
+ //
+ // Make sure that if a SIGKILL is pending, the process is killed !
+ //
+
+ if (Pending & PENDING_SIGKILL) {
+ SigAsMask = PENDING_SIGKILL;
+ }
+
+ //
+ // Clear the signal
+ //
+
+ p->SignalDataBase.PendingSignalMask &= ~SigAsMask;
+
+ switch ((ULONG)p->SignalDataBase.SignalDisposition[Signal-1].sa_handler) {
+
+ case (ULONG)SIG_IGN:
+
+ //
+ // If signal is being ignored, then simply clearing its
+ // pending bit "handles" the signal
+ //
+
+ break;
+
+ case (ULONG)SIG_DFL:
+
+ //
+ // Do the default action associated with the signal
+ //
+
+ switch (Signal) {
+
+ case SIGABRT:
+ case SIGALRM:
+ case SIGFPE:
+ case SIGHUP:
+ case SIGILL:
+ case SIGINT:
+ case SIGKILL:
+ case SIGPIPE:
+ case SIGQUIT:
+ case SIGSEGV:
+ case SIGTERM:
+ case SIGUSR1:
+ case SIGUSR2:
+
+ //
+ // Terminate Process. No reply will be generated. API Port
+ // can be closed in terminate process.
+ //
+
+ ReleaseProcessLock(p);
+ PsxTerminateProcessBySignal(p, m, Signal);
+ return TRUE;
+
+ case SIGCHLD:
+
+ //
+ // Default is to ignore this signal. Simply clear from
+ // pending mask.
+ //
+
+ break;
+
+ case SIGSTOP:
+ PsxStopProcess(p, m, SIGSTOP, RestoreBlockSigset);
+ return TRUE;
+
+ case SIGTSTP:
+ case SIGTTIN:
+ case SIGTTOU:
+
+ if (PsxStopProcess(p, m, Signal, RestoreBlockSigset)) {
+ return TRUE;
+ }
+ AcquireProcessLock(p);
+ break;
+
+ case SIGCONT:
+ //
+ // Since process can not be active, and stopped at the same
+ // time, a pending SIGCONT means that process is not
+ // stopped so simply ignore the signal.
+ //
+
+ break;
+
+ default:
+ Panic("Unknown Pending Signal");
+ break;
+ }
+ break;
+
+ default:
+
+ //
+ // Signal is being caught
+ //
+
+ if ((Signal == SIGKILL) || (Signal == SIGSTOP)) {
+ Panic("Catching SIGKILL or SIGSTOP");
+ }
+
+ if (m->Signal == SIGCONT) {
+ // When a signal is being caught, we should
+ // EINTR out.
+
+ m->Signal = SIGALRM;
+ }
+
+ PsxDeliverSignal(p,Signal,RestoreBlockSigset);
+ ReleaseProcessLock(p);
+ return FALSE;
+ }
+
+ }
+ }
+
+ ReleaseProcessLock(p);
+ return FALSE;
+}
+
+int
+PsxCheckPendingSignals(
+ IN PPSX_PROCESS p
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure is called from BlockProcess to see if the block should
+ proceed, or if the block should be aborted due to a signal.
+
+ This function is called with the process locked.
+
+Arguments:
+
+ p - Supplies the address of the process to be checked.
+
+Return Value:
+
+ Returns the number of the last pending signal, or 0 if there were no
+ pending signals.
+
+--*/
+
+{
+ sigset_t Pending;
+ ULONG Signal;
+ ULONG SigAsMask;
+ int ReturnSignal = 0;
+
+ Pending = p->SignalDataBase.PendingSignalMask &
+ ~p->SignalDataBase.BlockedSignalMask;
+
+ if (!Pending) {
+ return 0; // no pending, unblocked signals
+ }
+
+ for(Signal = 1, SigAsMask = 1; Signal <= _SIGMAXSIGNO;
+ Signal++, SigAsMask <<= 1L) {
+ if (!(Pending & SigAsMask)) {
+ continue; // this signal is not pending
+ }
+
+ switch ((ULONG)p->SignalDataBase.SignalDisposition[Signal - 1].sa_handler) {
+ case (ULONG)SIG_IGN:
+
+ //
+ // If signal is being ignored, then simply
+ // clearing its pending bit "handles" the
+ // signal.
+ //
+
+ p->SignalDataBase.PendingSignalMask &=
+ ~SigAsMask;
+ break;
+
+ case (ULONG)SIG_DFL:
+
+ switch (Signal) {
+ case SIGABRT:
+ case SIGALRM:
+ case SIGFPE:
+ case SIGHUP:
+ case SIGILL:
+ case SIGINT:
+ case SIGKILL:
+ case SIGPIPE:
+ case SIGQUIT:
+ case SIGSEGV:
+ case SIGTERM:
+ case SIGUSR1:
+ case SIGUSR2:
+
+ //
+ // The default action is to terminate. Don't
+ // let the block proceed.
+ //
+
+ break;
+
+ case SIGCHLD:
+
+ //
+ // Default is to ignore this signal. Clear
+ // from pending mask.
+ //
+
+ p->SignalDataBase.PendingSignalMask &=
+ ~SigAsMask;
+ continue;
+
+ case SIGSTOP:
+ case SIGTSTP:
+ case SIGTTIN:
+ case SIGTTOU:
+ //
+ // Default action is to stop process. Don't
+ // allow the process to be blocked.
+ //
+ break;
+
+ case SIGCONT:
+ //
+ // SIGCONT is defaulted; the action is to
+ // continue if stopped, ignore otherwise.
+ // Here we suspect that the process is being
+ // blocked, so we prevent that from happening.
+ //
+ break;
+
+ default:
+ Panic("Unknown Pending Signal");
+ break;
+ }
+
+ ReturnSignal = Signal;
+ break;
+
+ default:
+ //
+ // This signal is handled.
+ //
+
+ ReturnSignal = Signal;
+ }
+ }
+ return ReturnSignal;
+}
+
+VOID
+PsxDeliverSignal(
+ IN PPSX_PROCESS p,
+ IN ULONG Signal,
+ IN sigset_t *RestoreBlockSigset OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This function is used to deliver a signal to a process.
+ This function is only called from PendingSignalHandledInside. It
+ can safely assume that the target process is inside Psx.
+
+ This function is called with the process locked.
+
+Arguments:
+
+ p - Supplies the address of the process to be signaled
+
+ Signal - Supplies the index of the signal to be delivered to the
+ process
+
+ RestoreBlockSigset - Supplies an optional blocked signal mask that
+ should be restored after the signal completes
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS st;
+ sigset_t PreviousBlockMask;
+ ULONG Args[3];
+
+
+ PreviousBlockMask = ARGUMENT_PRESENT(RestoreBlockSigset) ?
+ *RestoreBlockSigset :
+ p->SignalDataBase.BlockedSignalMask;
+
+ //
+ // 1003.1-90 (3.3.4.2) -- When a signal is caught ... [the]
+ // mask is formed by taking the union of the current signal
+ // mask and the value of the sa_mask for the signal being
+ // delivered, and then including the signal being delivered.
+ //
+
+ p->SignalDataBase.BlockedSignalMask |=
+ p->SignalDataBase.SignalDisposition[Signal-1].sa_mask;
+
+ SIGADDSET(&p->SignalDataBase.BlockedSignalMask, Signal);
+
+ //
+ // Arrange for call to signal deliverer
+ //
+ // r5 = PreviousBlockMask
+ // r6 = Signal
+ // r7 = Handler
+ //
+
+
+ {
+ //
+ // XXX.mjb: this code seems to fix problems in the MIPS
+ // PP/signal_con test... I can't imagine why.
+ //
+ LARGE_INTEGER DelayInterval;
+
+ DelayInterval.HighPart = 0;
+ DelayInterval.LowPart = 0;
+
+ NtDelayExecution(TRUE, &DelayInterval);
+ }
+
+ Args[0] = (ULONG)PreviousBlockMask;
+ Args[1] = (ULONG)Signal;
+ Args[2] = (ULONG)(p->SignalDataBase.SignalDisposition[Signal-1].sa_handler);
+
+ st = RtlRemoteCall(
+ p->Process,
+ p->Thread,
+ (PVOID)p->SignalDeliverer,
+ 3,
+ Args,
+ TRUE,
+ TRUE
+ );
+
+ if (!NT_SUCCESS(st)) {
+ KdPrint(("PSXSS: PsxDeliverSignal: RtlRemoteCall: 0x%x\n", st));
+ }
+}
+
+VOID
+PsxSigSuspendHandler(
+ IN PPSX_PROCESS p,
+ IN PINTCB IntControlBlock,
+ IN PSX_INTERRUPTREASON InterruptReason,
+ IN int Signal // Signal interrupting, if any
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure is called when a signal is generated that should pop a
+ process out of a sigsuspend system call. The procedure is called with the
+ process locked. The main purpose of this function is to restore the
+ blocked signal mask to its original value, deallocate the interrupt control
+ block, and reply to the original call to sigsuspend. During the reply, the
+ generated signal will be deliverd, or will cause the process to terminate.
+
+ This function is responsible for unlocking the process.
+
+Arguments:
+
+ p - Supplies the address of the process being interrupted.
+
+ IntControlBlock - Supplies the address of the interrupt control block.
+
+ InterruptReason - Supplies the reason that this process is being
+ interrupted. Not used in this handler.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PPSX_API_MSG m;
+ sigset_t RestoreBlockSigset;
+
+ RtlLeaveCriticalSection(&BlockLock);
+
+ RestoreBlockSigset = (sigset_t)IntControlBlock->IntContext;
+
+ m = IntControlBlock->IntMessage;
+ RtlFreeHeap(PsxHeap, 0,IntControlBlock);
+
+ m->Error = EINTR;
+ m->Signal = Signal;
+ ApiReply(p, m, &RestoreBlockSigset);
+ RtlFreeHeap(PsxHeap, 0, m);
+}
diff --git a/private/posix/psxss/sources b/private/posix/psxss/sources
new file mode 100644
index 000000000..67a3e1c47
--- /dev/null
+++ b/private/posix/psxss/sources
@@ -0,0 +1,80 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=posix
+MINORCOMP=psxss
+
+TARGETNAME=psxss
+TARGETPATH=obj
+TARGETTYPE=PROGRAM
+
+INCLUDES=..\inc;..\..\inc;$(BASEDIR)\public\sdk\inc\posix;$(BASEDIR)\public\sdk\inc\posix\sys
+
+SOURCES= \
+ acledit.c \
+ apiinit.c \
+ apilistn.c \
+ apireqst.c \
+ concreat.c \
+ coninit.c \
+ conio.c \
+ consignl.c \
+ conthrds.c \
+ fdio.c \
+ fileio.c \
+ flocks.c \
+ lpipeio.c \
+ nullio.c \
+ procblk.c \
+ process.c \
+ psxss.c \
+ psxss.rc \
+ psxsup.c \
+ sbapi.c \
+ sbinit.c \
+ sbreqst.c \
+ session.c \
+ sigapi.c \
+ sigsup.c \
+ srvdebug.c \
+ srvfile.c \
+ srvhandl.c \
+ srvinit.c \
+ srvtask.c \
+ srvtc.c \
+ stub.c \
+ sysdb.c \
+ timer.c \
+ psxss.rc
+
+C_DEFINES=-DPSX=1 -D_POSIX_
+UMTYPE=windows
+
+TARGETLIBS= \
+ $(BASEDIR)\public\sdk\lib\*\smdll.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\samlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntdll.lib
+
+COFFBASE=psxss
diff --git a/private/posix/psxss/srvdebug.c b/private/posix/psxss/srvdebug.c
new file mode 100644
index 000000000..4144a1f52
--- /dev/null
+++ b/private/posix/psxss/srvdebug.c
@@ -0,0 +1,43 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ psxdbg.c
+
+Abstract:
+
+ This module implements debugger support routines for
+ posix.
+
+Author:
+
+ Mark Lucovsky (markl) 05-Feb-1990
+
+Revision History:
+
+--*/
+
+#include "psxsrv.h"
+#include <ntdbg.h>
+
+
+
+NTSTATUS
+PsxpDebugUiLookup(
+ IN PCLIENT_ID AppClientId,
+ OUT PCLIENT_ID DebugUiClientId
+ )
+{
+
+ PPSX_PROCESS p;
+
+ p = PsxLocateProcessByClientId(AppClientId);
+
+ ASSERT(p && p->ProcessIsBeingDebugged);
+
+ *DebugUiClientId = p->DebugUiClientId;
+
+ return STATUS_SUCCESS;
+}
diff --git a/private/posix/psxss/srvfile.c b/private/posix/psxss/srvfile.c
new file mode 100644
index 000000000..3b56e417f
--- /dev/null
+++ b/private/posix/psxss/srvfile.c
@@ -0,0 +1,1152 @@
+#include "psxsrv.h"
+#include <sys/stat.h>
+
+
+NTSTATUS
+DoAccess(
+ HANDLE FileHandle,
+ PPSX_PROCESS p,
+ mode_t Mode,
+ PULONG pError
+ );
+
+BOOLEAN
+DumpFileIfRequired(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m,
+ IN HANDLE FileHandle,
+ IN PUNICODE_STRING Path_U
+ );
+
+static BOOLEAN
+IsUserInGroup(
+ PPSX_PROCESS p,
+ PSID Group
+ );
+
+static PVOID pvSDMem[10]; // InitSecurityDescriptor -> DeInitSD
+
+BOOLEAN
+FilesAreIdentical(
+ HANDLE FileA,
+ HANDLE FileB
+ )
+{
+ FILE_INTERNAL_INFORMATION infoA, infoB;
+ IO_STATUS_BLOCK Iosb;
+ NTSTATUS Status;
+
+ Status = NtQueryInformationFile(FileA, &Iosb, &infoA,
+ sizeof(infoA), FileInternalInformation);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = NtQueryInformationFile(FileB, &Iosb, &infoB,
+ sizeof(infoB), FileInternalInformation);
+ ASSERT(NT_SUCCESS(Status));
+
+ return infoA.IndexNumber.QuadPart == infoB.IndexNumber.QuadPart;
+}
+
+BOOLEAN
+PsxUnlink(
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m
+ )
+{
+ PPSX_UNLINK_MSG args;
+ HANDLE FileHandle,
+ DirHandle;
+ UNICODE_STRING
+ ParentDir_U; // path to the containing directory
+ OBJECT_ATTRIBUTES
+ DirObj, // names the parent directory
+ FileObj; // names the file to be unlinked
+ IO_STATUS_BLOCK Iosb;
+ NTSTATUS Status;
+ FILE_DISPOSITION_INFORMATION Disp;
+ ANSI_STRING Path_A;
+ PCHAR pch;
+ ULONG Error;
+
+ args = &m->u.Unlink;
+
+ if (!ISPOINTERVALID_CLIENT(p, args->Path_U.Buffer,
+ args->Path_U.Length)) {
+ KdPrint(("Invalid pointer to unlink: %lx, %d\n",
+ args->Path_U.Buffer, args->Path_U.Length));
+ m->Error = EINVAL;
+ return TRUE;
+ }
+
+ Status = RtlUnicodeStringToAnsiString(&Path_A, &args->Path_U, TRUE);
+ if (!NT_SUCCESS(Status)) {
+ m->Error = ENOMEM;
+ return TRUE;
+ }
+
+ if ('\\' == Path_A.Buffer[Path_A.Length - 1]) {
+ //
+ // Unlink must reject paths that end in slash.
+ //
+ RtlFreeAnsiString(&Path_A);
+ m->Error = EINVAL;
+ return TRUE;
+ }
+ RtlFreeAnsiString(&Path_A);
+
+ Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
+ ASSERT(NT_SUCCESS(Status));
+
+ InitializeObjectAttributes(&FileObj, &args->Path_U, 0, NULL, NULL);
+ Status = NtOpenFile(&FileHandle, DELETE | SYNCHRONIZE, &FileObj,
+ &Iosb, SHARE_ALL,
+ FILE_SYNCHRONOUS_IO_ALERT | FILE_NON_DIRECTORY_FILE);
+
+ EndImpersonation();
+
+ if (!NT_SUCCESS(Status)) {
+ if (STATUS_FILE_IS_A_DIRECTORY == Status) {
+ m->Error = EPERM;
+ return TRUE;
+ }
+ if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
+ m->Error = PsxStatusToErrnoPath(&args->Path_U);
+ return TRUE;
+ }
+ m->Error = PsxStatusToErrno(Status);
+ return TRUE;
+ }
+
+ //
+ // If some posix process has this file open, we can't delete
+ // it or that process would be unable to write to the file (the
+ // only operation allowed on a deleted file is "close").
+ //
+
+ if (DumpFileIfRequired(p, m, FileHandle, &args->Path_U)) {
+ NtClose(FileHandle);
+ return TRUE;
+ }
+
+ //
+ // If we didn't dump the file, we'll try to delete it in the
+ // obvious way. Should succeed unless some windows process has
+ // it open -- we don't worry about that possibility, you get
+ // what you get.
+ //
+
+ Disp.DeleteFile = TRUE;
+ Status = NtSetInformationFile(FileHandle, &Iosb, &Disp, sizeof(Disp),
+ FileDispositionInformation);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: PsxUnlink: SetInfoFile: 0x%x\n", Status));
+ m->Error = PsxStatusToErrno(Status);
+ return TRUE;
+ }
+ NtClose(FileHandle);
+ return TRUE;
+}
+
+
+NTSTATUS
+DoAccess(
+ HANDLE FileHandle,
+ PPSX_PROCESS p,
+ mode_t Mode,
+ PULONG pError
+ )
+{
+ FILE_ACCESS_INFORMATION FileAccessInfo;
+ BOOLEAN RetVal;
+ SECURITY_INFORMATION SecurityInformation;
+ PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
+ PSID NtOwner, NtGroup;
+ BOOLEAN OwnerDefaulted, GroupDefaulted;
+ BOOLEAN DaclPresent, DaclDefaulted;
+ ACCESS_MASK UserAccess, GroupAccess, OtherAccess;
+ ULONG LengthNeeded;
+ mode_t filemode;
+ PSID UserSid;
+ PACL pDacl;
+ NTSTATUS Status;
+ HANDLE TokenHandle;
+ PSID_AND_ATTRIBUTES pSA;
+
+ //
+ // N.B.: this code kind of depends on F_OK being 0.
+ //
+
+ if (F_OK == Mode) {
+ //
+ // The file exists: succeed.
+ //
+ return TRUE;
+ }
+
+ Status = NtOpenProcessToken(p->Process, GENERIC_READ, &TokenHandle);
+ if (!NT_SUCCESS(Status)) {
+ // can fail due to lack of memory
+ *pError = ENOMEM;
+ return Status;
+ }
+
+ Status = NtQueryInformationToken(TokenHandle, TokenUser, NULL,
+ 0, &LengthNeeded);
+ if (STATUS_BUFFER_TOO_SMALL != Status) {
+ NtClose(TokenHandle);
+ *pError = PsxStatusToErrno(Status);
+ return Status;
+ }
+
+ pSA = RtlAllocateHeap(PsxHeap, 0, LengthNeeded);
+ if (NULL == pSA) {
+ *pError = ENOMEM;
+ NtClose(TokenHandle);
+ return STATUS_NO_MEMORY;
+ }
+
+ Status = NtQueryInformationToken(TokenHandle, TokenUser, pSA,
+ LengthNeeded, &LengthNeeded);
+ ASSERT(NT_SUCCESS(Status));
+
+ UserSid = pSA->Sid;
+
+ //
+ // Get the security descriptor for the file.
+ //
+
+ SecurityInformation = OWNER_SECURITY_INFORMATION |
+ GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
+
+ Status = NtQuerySecurityObject(FileHandle, SecurityInformation,
+ NULL, 0, &LengthNeeded);
+ if (!(STATUS_BUFFER_TOO_SMALL == Status)) {
+ KdPrint(("PSXSS: NtQSObject failed: 0x%x\n", Status));
+ }
+ ASSERT(STATUS_BUFFER_TOO_SMALL == Status);
+
+ SecurityDescriptor = RtlAllocateHeap(PsxHeap, 0, LengthNeeded);
+ if (NULL == SecurityDescriptor) {
+ NtClose(FileHandle);
+ *pError = ENOMEM;
+ return STATUS_NO_MEMORY;
+ }
+
+ Status = NtQuerySecurityObject(FileHandle, SecurityInformation,
+ SecurityDescriptor, LengthNeeded, &LengthNeeded);
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Get the owner and group from the security descriptor
+ //
+
+ Status = RtlGetOwnerSecurityDescriptor(SecurityDescriptor,
+ &NtOwner, &OwnerDefaulted);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = RtlGetGroupSecurityDescriptor(SecurityDescriptor,
+ &NtGroup, &GroupDefaulted);
+ ASSERT(NT_SUCCESS(Status));
+
+ if (NULL == NtOwner || NULL == NtGroup) {
+ //
+ // Seems like this file doesn't have an owner or a
+ // group, which means that we can't change it's mode.
+ //
+ NtClose(FileHandle);
+ *pError = EPERM;
+ return TRUE;
+ }
+
+ Status = RtlGetDaclSecurityDescriptor(SecurityDescriptor,
+ &DaclPresent, &pDacl, &DaclDefaulted);
+ ASSERT(NT_SUCCESS(Status));
+
+ if (!DaclPresent || NULL == pDacl) {
+ //
+ // In this case, all access is granted; we don't even look
+ // to see what particular access the caller was interested
+ // in.
+ //
+ return TRUE;
+ }
+
+ Status = RtlInterpretPosixAcl(ACL_REVISION2, NtOwner, NtGroup,
+ pDacl, &UserAccess, &GroupAccess, &OtherAccess);
+ if (!NT_SUCCESS(Status)) {
+ //
+ // XXX.mjb: Should do something reasonable here.
+ //
+ return TRUE;
+ }
+
+ filemode = AccessMaskToMode(UserAccess, GroupAccess, OtherAccess);
+
+ if (RtlEqualSid(UserSid, NtOwner)) {
+ if (Mode & (filemode >> 6)) {
+ // access is granted.
+ return STATUS_SUCCESS;
+ }
+ }
+ if (IsUserInGroup(p, NtGroup)) {
+ if (Mode & (filemode >> 3)) {
+ // access is granted.
+ return STATUS_SUCCESS;
+ }
+ }
+ if (Mode & filemode) {
+ // access is granted.
+ return STATUS_SUCCESS;
+ }
+
+ *pError = EACCES;
+ return STATUS_UNSUCCESSFUL;
+}
+
+//
+// See if the given group is one that the owner of this process belongs
+// to.
+//
+static BOOLEAN
+IsUserInGroup(
+ PPSX_PROCESS p,
+ PSID Group
+ )
+{
+ HANDLE TokenHandle;
+ TOKEN_GROUPS *pGroups;
+ ULONG LengthNeeded;
+ NTSTATUS Status;
+ BOOLEAN RetVal = FALSE;
+ ULONG i;
+
+ Status = NtOpenProcessToken(p->Process, GENERIC_READ, &TokenHandle);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = NtQueryInformationToken(TokenHandle, TokenGroups, NULL,
+ 0, &LengthNeeded);
+ ASSERT(STATUS_BUFFER_TOO_SMALL == Status);
+
+ pGroups = RtlAllocateHeap(PsxHeap, 0, LengthNeeded);
+ if (NULL == pGroups) {
+ NtClose(TokenHandle);
+ return FALSE;
+ }
+
+ Status = NtQueryInformationToken(TokenHandle, TokenGroups, pGroups,
+ LengthNeeded, &LengthNeeded);
+ ASSERT(NT_SUCCESS(Status));
+
+ for (i = 0; i < pGroups->GroupCount; ++i) {
+ if (RtlEqualSid(pGroups->Groups[i].Sid, Group)) {
+ RetVal = TRUE;
+ break;
+ }
+ }
+
+ RtlFreeHeap(PsxHeap, 0, pGroups);
+ NtClose(TokenHandle);
+ return RetVal;
+}
+
+
+
+BOOLEAN
+PsxRmDir(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+/*++
+
+Routine Description:
+
+ This procedure implements POSIX rmdir.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the rmdir request.
+
+Return Value:
+
+ TRUE - The contents of *m should be used to generate a reply.
+
+--*/
+{
+ PPSX_RMDIR_MSG args;
+ HANDLE FileHandle, ParentHandle;
+ NTSTATUS Status;
+ IO_STATUS_BLOCK Iosb;
+ OBJECT_ATTRIBUTES
+ ParentObj,
+ DirObj;
+ UNICODE_STRING
+ ParentDir_U,
+ Path_U;
+ FILE_DISPOSITION_INFORMATION DispositionInfo;
+ FILE_INTERNAL_INFORMATION SerialNumber;
+ PIONODE IoNode;
+ UCHAR buf[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) +
+ 128 * sizeof(WCHAR)];
+ PFILE_FS_ATTRIBUTE_INFORMATION pFSInfo = (PVOID)buf;
+ ANSI_STRING Path_A;
+ PCHAR pch;
+
+ args = &m->u.RmDir;
+
+ if (!ISPOINTERVALID_CLIENT(p,args->Path_U.Buffer,args->Path_U.Length)) {
+ KdPrint(("Invalid pointer to rmdir %lx \n",
+ args->Path_U.Buffer));
+ m->Error = EINVAL;
+ return TRUE;
+ }
+
+ Path_U = args->Path_U;
+
+ Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
+ ASSERT(NT_SUCCESS(Status));
+
+ InitializeObjectAttributes(&DirObj, &Path_U, 0, NULL, NULL);
+
+ Status = NtOpenFile(&FileHandle, SYNCHRONIZE | DELETE,
+ &DirObj, &Iosb,
+ SHARE_ALL,
+ FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
+
+ EndImpersonation();
+
+ if (!NT_SUCCESS(Status)) {
+ if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
+ m->Error = PsxStatusToErrnoPath(&Path_U);
+ return TRUE;
+ }
+ m->Error = PsxStatusToErrno(Status);
+ return TRUE;
+ }
+
+ //
+ // If some posix process has this dir open, we can't delete
+ // it or that process would be unable to read from the dir (the
+ // only operation allowed on a deleted dir is "close".
+ //
+
+//
+// XXX.mjb: must check to make sure the directory contains no files,
+// and return ENOTEMPTY or EEXIST if so.
+//
+
+ if (DumpFileIfRequired(p, m, FileHandle, &Path_U)) {
+ NtClose(FileHandle);
+ return TRUE;
+ }
+
+ m->Error = 0;
+
+ DispositionInfo.DeleteFile = TRUE;
+
+ Status = NtSetInformationFile(FileHandle, &Iosb,
+ &DispositionInfo, sizeof(DispositionInfo),
+ FileDispositionInformation);
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (STATUS_CANNOT_DELETE == Status) {
+
+ //
+ // 1003.1-90 (5.5.2.2) : If the named directory is the root
+ // directory or the current working directory of any process, it
+ // is unspecified whether the function succeeds or whether it fails
+ // and sets /errno/ to [EBUSY].
+ //
+ // NT is going to fail with STATUS_CANNOT_DELETE here, and so we
+ // want posix to return EBUSY. (The standard posix mapping is to
+ // ETXTBUSY.)
+ //
+
+ m->Error = EBUSY;
+
+ } else {
+ m->Error = PsxStatusToErrno(Status);
+ }
+
+ }
+
+ Status = NtClose(FileHandle);
+ ASSERT(NT_SUCCESS(Status));
+
+ return TRUE;
+}
+
+
+BOOLEAN
+PsxAccess(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+/*++
+
+Routine Description:
+
+ This procedure implements POSIX access.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the access request.
+
+Return Value:
+
+ TRUE - The contents of *m should be used to generate a reply.
+
+--*/
+{
+ PPSX_ACCESS_MSG args;
+ HANDLE FileHandle;
+ NTSTATUS Status;
+ IO_STATUS_BLOCK Iosb;
+ OBJECT_ATTRIBUTES ObjA;
+ UNICODE_STRING Path_U;
+
+ args = &m->u.Access;
+
+ if (!ISPOINTERVALID_CLIENT(p,args->Path_U.Buffer,args->Path_U.Length)) {
+ KdPrint(("Invalid pointer to access: %lx\n",
+ args->Path_U.Buffer));
+ m->Error = EINVAL;
+ return TRUE;
+ }
+
+ Path_U = args->Path_U;
+
+ InitializeObjectAttributes(&ObjA, &Path_U, 0, NULL, NULL);
+
+ Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
+ ASSERT(NT_SUCCESS(Status));
+ {
+ Status = NtOpenFile(&FileHandle,
+ SYNCHRONIZE | READ_CONTROL | FILE_READ_ATTRIBUTES,
+ &ObjA, &Iosb,
+ SHARE_ALL,
+ FILE_SYNCHRONOUS_IO_NONALERT);
+ } EndImpersonation();
+
+ if (!NT_SUCCESS(Status)) {
+ if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
+ m->Error = PsxStatusToErrnoPath(&Path_U);
+ return TRUE;
+ }
+ m->Error = PsxStatusToErrno(Status);
+ return TRUE;
+ }
+
+ (void)DoAccess(FileHandle, p, args->Amode, &m->Error);
+
+ NtClose(FileHandle);
+
+ return TRUE;
+}
+
+
+BOOLEAN
+PsxRename(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+/*++
+
+Routine Description:
+
+ This procedure implements POSIX rename. The routine must
+ be implemented on the server because if we're renaming onto
+ an open file, we need to move it to the dump first.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the access request.
+
+Return Value:
+
+ TRUE - The contents of *m should be used to generate a reply.
+
+--*/
+{
+ PPSX_RENAME_MSG args;
+ HANDLE FileHandle, NewFileHandle, RootDirHandle;
+ NTSTATUS Status;
+ IO_STATUS_BLOCK Iosb;
+ OBJECT_ATTRIBUTES Obj;
+ ANSI_STRING str_A, rel_A;
+ UNICODE_STRING str_U, new_U;
+ PFILE_RENAME_INFORMATION pRenameInfo;
+ char *pch, ch;
+
+ FILE_INTERNAL_INFORMATION SerialNumber;
+ unsigned char buf[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) +
+ 128*sizeof(WCHAR)];
+ PFILE_FS_ATTRIBUTE_INFORMATION pFSInfo = (PVOID)buf;
+ PIONODE IoNode;
+
+ args = &m->u.Rename;
+
+ //
+ // Open the old pathname
+ //
+
+ InitializeObjectAttributes(&Obj, &args->OldName, 0, NULL, NULL);
+
+ Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = NtOpenFile(&FileHandle, SYNCHRONIZE | DELETE,
+ &Obj, &Iosb, SHARE_ALL, FILE_SYNCHRONOUS_IO_NONALERT);
+
+ EndImpersonation();
+
+ if (!NT_SUCCESS(Status)) {
+ if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
+ m->Error = PsxStatusToErrnoPath(&args->OldName);
+ return TRUE;
+ }
+ m->Error = PsxStatusToErrno(Status);
+ return TRUE;
+ }
+
+ //
+ // Now we need to get a handle on the root directory of the 'new'
+ // path; we'll pass that in the rename information, and the rest
+ // of the path will be given relative to that root. We depend
+ // on paths looking like "\DosDevices\X:\path".
+ //
+
+ //
+ // Open the new filename for error checking, then close. We
+ // also take this opportunity to find the ENOTDIR error case.
+ //
+
+ InitializeObjectAttributes(&Obj, &args->NewName, 0, NULL, NULL);
+
+ Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = NtOpenFile(&NewFileHandle, SYNCHRONIZE,
+ &Obj, &Iosb, SHARE_ALL, FILE_SYNCHRONOUS_IO_NONALERT);
+
+ EndImpersonation();
+
+ if (NT_SUCCESS(Status)) {
+ if (FilesAreIdentical(FileHandle, NewFileHandle)) {
+ //
+ // 1003.1-90 (5.5.3.1): If the old argument and the
+ // new argument both refer to links to the same
+ // existing file, the rename function shall return
+ // successfully and perform no other action.
+ //
+
+ NtClose(FileHandle);
+ NtClose(NewFileHandle);
+ m->ReturnValue = 0;
+ return TRUE;
+ }
+
+ DumpFileIfRequired(p, m, NewFileHandle, &args->NewName);
+ m->Error = 0;
+ NtClose(NewFileHandle);
+ } else if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
+ m->Error = PsxStatusToErrnoPath(&args->NewName);
+ if (ENOTDIR == m->Error) {
+ NtClose(FileHandle);
+ return TRUE;
+ }
+ m->Error = 0;
+ }
+
+ Status = RtlUnicodeStringToAnsiString(&str_A, &args->NewName, TRUE);
+ if (!NT_SUCCESS(Status)) {
+ NtClose(FileHandle);
+ m->Error = ENOMEM;
+ return TRUE;
+ }
+
+ // find the root directory
+
+ pch = strchr(str_A.Buffer + 1, '\\');
+ ASSERT(NULL != pch);
+ pch = strchr(pch + 1, '\\');
+ ASSERT(NULL != pch);
+ ch = pch[1];
+ pch[1] = '\000';
+ str_A.Length = strlen(str_A.Buffer);
+
+ Status = RtlAnsiStringToUnicodeString(&str_U, &str_A, TRUE);
+ if (!NT_SUCCESS(Status)) {
+ RtlFreeAnsiString(&str_A);
+ NtClose(FileHandle);
+ m->Error = ENOMEM;
+ return TRUE;
+ }
+
+ InitializeObjectAttributes(&Obj, &str_U, 0, NULL, NULL);
+
+ Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = NtOpenFile(&RootDirHandle, SYNCHRONIZE, &Obj, &Iosb,
+ SHARE_ALL, FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
+
+ EndImpersonation();
+
+ RtlFreeUnicodeString(&str_U);
+ if (!NT_SUCCESS(Status)) {
+ RtlFreeAnsiString(&str_A);
+ NtClose(FileHandle);
+ m->Error = PsxStatusToErrno(Status);
+ return TRUE;
+ }
+
+ //
+ // Now get the path relative to the root.
+ //
+
+ pch[1] = ch;
+ rel_A.Buffer = &pch[1];
+ rel_A.Length = rel_A.MaximumLength = strlen(rel_A.Buffer);
+ Status = RtlAnsiStringToUnicodeString(&str_U, &rel_A, TRUE);
+ RtlFreeAnsiString(&str_A);
+ if (!NT_SUCCESS(Status)) {
+ NtClose(RootDirHandle);
+ NtClose(FileHandle);
+ m->Error = ENOMEM;
+ return TRUE;
+ }
+
+ pRenameInfo = RtlAllocateHeap(PsxHeap, 0,
+ sizeof(*pRenameInfo) + str_U.Length);
+ if (NULL == pRenameInfo) {
+ RtlFreeUnicodeString(&str_U);
+ NtClose(RootDirHandle);
+ NtClose(FileHandle);
+ m->Error = ENOMEM;
+ return TRUE;
+ }
+
+ RtlMoveMemory(pRenameInfo->FileName, str_U.Buffer, str_U.Length);
+ pRenameInfo->FileNameLength = str_U.Length;
+
+ pRenameInfo->ReplaceIfExists = TRUE;
+ pRenameInfo->RootDirectory = RootDirHandle;
+
+ Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = NtSetInformationFile(FileHandle, &Iosb, pRenameInfo,
+ sizeof(*pRenameInfo) + str_U.Length,
+ FileRenameInformation);
+
+ RtlFreeUnicodeString(&str_U);
+
+ EndImpersonation();
+
+ NtClose(FileHandle);
+ NtClose(RootDirHandle);
+ RtlFreeHeap(PsxHeap, 0, pRenameInfo);
+
+ if (!NT_SUCCESS(Status)) {
+ m->Error = PsxStatusToErrno(Status);
+ }
+ return TRUE;
+}
+
+BOOLEAN
+PsxLink(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+{
+ PPSX_LINK_MSG args;
+ HANDLE FileHandle, NewFileHandle, RootDirHandle;
+ NTSTATUS Status;
+ IO_STATUS_BLOCK Iosb;
+ OBJECT_ATTRIBUTES Obj;
+ PFILE_LINK_INFORMATION pLinkInfo;
+ FILE_INTERNAL_INFORMATION SerialNumber;
+ LARGE_INTEGER Time;
+ ULONG PosixTime;
+ PIONODE IoNode;
+ UNICODE_STRING str_U, new_U;
+ ANSI_STRING str_A, rel_A;
+ char *pch, ch;
+
+ args = &m->u.Link;
+
+ //
+ // Open the existing pathname.
+ //
+
+ InitializeObjectAttributes(&Obj, &args->OldName, 0, NULL, NULL);
+
+ Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = NtOpenFile(&FileHandle, SYNCHRONIZE, &Obj, &Iosb,
+ SHARE_ALL,
+ FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
+ EndImpersonation();
+
+ if (!NT_SUCCESS(Status)) {
+ if (STATUS_FILE_IS_A_DIRECTORY == Status) {
+ m->Error = EPERM;
+ return TRUE;
+ }
+ if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
+ m->Error = PsxStatusToErrnoPath(&args->OldName);
+ return TRUE;
+ }
+ m->Error = PsxStatusToErrno(Status);
+ return TRUE;
+ }
+
+ //
+ // Open new filename for error checking, then close. This
+ // is to find the ENOTDIR error case.
+ //
+
+ InitializeObjectAttributes(&Obj, &args->NewName, 0, NULL, NULL);
+
+ Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = NtOpenFile(&NewFileHandle, SYNCHRONIZE,
+ &Obj, &Iosb, SHARE_ALL, FILE_SYNCHRONOUS_IO_NONALERT);
+
+ EndImpersonation();
+
+ if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
+ m->Error = PsxStatusToErrnoPath(&args->NewName);
+ if (ENOTDIR == m->Error) {
+ NtClose(FileHandle);
+ return TRUE;
+ }
+ m->Error = 0;
+ } else if (NT_SUCCESS(Status)) {
+ NtClose(NewFileHandle);
+ }
+
+ //
+ // Now we need to get a handle on the root directory of the 'new'
+ // pathname; we'll pass that in the link information, and the
+ // rest of the path will be given relative to the root. We
+ // depend on paths looking like "\DosDevices\X:\path".
+ //
+
+ Status = RtlUnicodeStringToAnsiString(&str_A, &args->NewName, TRUE);
+ if (!NT_SUCCESS(Status)) {
+ NtClose(FileHandle);
+ m->Error = ENOMEM;
+ return TRUE;
+ }
+
+ // find the root directory
+
+ pch = strchr(str_A.Buffer + 1, '\\');
+ ASSERT(NULL != pch);
+ pch = strchr(pch + 1, '\\');
+ ASSERT(NULL != pch);
+ ch = pch[1];
+ pch[1] = '\0';
+ str_A.Length = strlen(str_A.Buffer);
+
+ Status = RtlAnsiStringToUnicodeString(&str_U, &str_A, TRUE);
+ if (!NT_SUCCESS(Status)) {
+ RtlFreeAnsiString(&str_A);
+ NtClose(FileHandle);
+ m->Error = ENOMEM;
+ return TRUE;
+ }
+
+ Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
+ ASSERT(NT_SUCCESS(Status));
+
+ InitializeObjectAttributes(&Obj, &str_U, 0, NULL, NULL);
+ Status = NtOpenFile(&RootDirHandle, SYNCHRONIZE, &Obj, &Iosb,
+ SHARE_ALL, FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
+ EndImpersonation();
+
+ RtlFreeUnicodeString(&str_U);
+ if (!NT_SUCCESS(Status)) {
+ RtlFreeAnsiString(&str_A);
+ NtClose(FileHandle);
+ m->Error = PsxStatusToErrno(Status);
+ return TRUE;
+ }
+
+ //
+ // Now get the path relative to the root.
+ //
+
+ pch[1] = ch;
+ rel_A.Buffer = &pch[1];
+ rel_A.Length = rel_A.MaximumLength = strlen(rel_A.Buffer);
+ Status = RtlAnsiStringToUnicodeString(&str_U, &rel_A, TRUE);
+ RtlFreeAnsiString(&str_A);
+ if (!NT_SUCCESS(Status)) {
+ NtClose(RootDirHandle);
+ NtClose(FileHandle);
+ m->Error = ENOMEM;
+ return TRUE;
+ }
+
+ pLinkInfo = RtlAllocateHeap(PsxHeap, 0, sizeof(*pLinkInfo) + str_U.Length);
+ if (NULL == pLinkInfo) {
+ RtlFreeUnicodeString(&str_U);
+ NtClose(RootDirHandle);
+ NtClose(FileHandle);
+ m->Error = ENOMEM;
+ return TRUE;
+ }
+
+ RtlMoveMemory(pLinkInfo->FileName, str_U.Buffer, str_U.Length);
+ pLinkInfo->FileNameLength = str_U.Length;
+
+ pLinkInfo->ReplaceIfExists = FALSE;
+ pLinkInfo->RootDirectory = RootDirHandle;
+
+ Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = NtSetInformationFile(FileHandle, &Iosb, pLinkInfo,
+ sizeof(*pLinkInfo) + str_U.Length, FileLinkInformation);
+
+ EndImpersonation();
+ NtClose(RootDirHandle);
+ RtlFreeHeap(PsxHeap, 0, pLinkInfo);
+ RtlFreeUnicodeString(&str_U);
+
+ if (!NT_SUCCESS(Status)) {
+ NtClose(FileHandle);
+ m->Error = PsxStatusToErrno(Status);
+ return TRUE;
+ }
+
+ Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = NtQueryInformationFile(FileHandle, &Iosb, &SerialNumber,
+ sizeof(SerialNumber), FileInternalInformation);
+
+ if (!NT_SUCCESS(Status)) {
+ NtClose(FileHandle);
+ m->Error = PsxStatusToErrno(Status);
+ return TRUE;
+ }
+
+ EndImpersonation();
+ NtClose(FileHandle);
+
+ if (ReferenceOrCreateIoNode(GetFileDeviceNumber(&args->OldName),
+ (ino_t)SerialNumber.IndexNumber.LowPart, TRUE, &IoNode)) {
+ // File is open.
+
+ NtQuerySystemTime(&Time);
+ if (!RtlTimeToSecondsSince1970(&Time, &PosixTime)) {
+ PosixTime = 0;
+ }
+
+ IoNode->ModifyIoNodeTime = PosixTime;
+
+ return TRUE;
+ }
+
+ return TRUE;
+}
+
+BOOLEAN
+DumpFileIfRequired(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m,
+ IN HANDLE FileHandle,
+ IN PUNICODE_STRING Path_U
+ )
+{
+ UNICODE_STRING U;
+ UNICODE_STRING Dir_U, New_U;
+ WCHAR wcBuf[100];
+ BOOLEAN Found;
+ OBJECT_ATTRIBUTES Obj;
+ HANDLE DirHandle;
+ NTSTATUS Status;
+ IO_STATUS_BLOCK Iosb;
+ PFILE_RENAME_INFORMATION pRenameInfo;
+ LARGE_INTEGER Time;
+ ULONG PosixTime;
+ CHAR sdbuf[SECURITY_DESCRIPTOR_MIN_LENGTH];
+ PSECURITY_DESCRIPTOR pSD = (PVOID)sdbuf;
+ FILE_INTERNAL_INFORMATION SerialNumber;
+ unsigned char buf[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) +
+ 128*sizeof(WCHAR)];
+ PFILE_FS_ATTRIBUTE_INFORMATION pFSInfo = (PVOID)buf;
+ PIONODE IoNode;
+ FILE_STANDARD_INFORMATION StandardInfo;
+
+ Status = NtQueryInformationFile(FileHandle, &Iosb, &SerialNumber,
+ sizeof(SerialNumber), FileInternalInformation);
+ ASSERT(NT_SUCCESS(Status));
+
+ if (!ReferenceOrCreateIoNode(GetFileDeviceNumber(Path_U),
+ (ino_t)SerialNumber.IndexNumber.LowPart, TRUE, &IoNode)) {
+
+ // File is not open.
+
+ return FALSE;
+ }
+
+ // File is open.
+
+ // If the filesystem is not NTFS, we don't bother with
+ // this crap.
+
+ Status = NtQueryVolumeInformationFile(FileHandle,
+ &Iosb, buf, sizeof(buf), FileFsAttributeInformation);
+ ASSERT(NT_SUCCESS(Status));
+
+ pFSInfo->FileSystemName[pFSInfo->FileSystemNameLength/2] = 0;
+ if (0 != wcscmp(L"NTFS", pFSInfo->FileSystemName)) {
+ // Not NTFS.
+
+ return FALSE;
+ }
+
+ //
+ // Create the dump on the root of the filesystem where the
+ // file is located.
+ //
+
+ U.MaximumLength = PATH_MAX * sizeof(WCHAR);
+ U.Buffer = RtlAllocateHeap(PsxHeap, HEAP_ZERO_MEMORY,
+ U.MaximumLength);
+ if (NULL == U.Buffer) {
+ m->Error = ENOMEM;
+ return FALSE;
+ }
+ wcsncpy(U.Buffer, Path_U->Buffer, wcslen(L"/DosDevices/X:/"));
+ wcscat(U.Buffer, PSX_JUNK_DIR);
+ U.Length = wcslen(U.Buffer) * sizeof(WCHAR);
+
+ Status = InitSecurityDescriptor(pSD, &U, NtCurrentProcess(),
+ S_IRWXU, pvSDMem);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: DumpFile: InitSD: 0x%x\n", Status));
+ m->Error = EPERM;
+ return FALSE;
+ }
+
+ InitializeObjectAttributes(&Obj, &U, 0, NULL, pSD);
+
+ Status = NtCreateFile(
+ &DirHandle,
+ SYNCHRONIZE,
+ &Obj,
+ &Iosb,
+ NULL,
+ FILE_ATTRIBUTE_SYSTEM,
+ SHARE_ALL,
+ FILE_OPEN_IF,
+ FILE_DIRECTORY_FILE,
+ NULL, 0
+ );
+
+ DeInitSecurityDescriptor(pSD, pvSDMem);
+
+ RtlFreeHeap(PsxHeap, 0, (PVOID)U.Buffer);
+
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: Can't create/open junk dir, 0x%x\n", Status));
+ m->Error = PsxStatusToErrno(Status);
+ return FALSE;
+ }
+
+ //
+ // Get a handle on the file.
+ //
+
+ InitializeObjectAttributes(&Obj, Path_U, 0, NULL, NULL);
+
+ Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = NtOpenFile(&FileHandle, SYNCHRONIZE | DELETE,
+ &Obj, &Iosb, SHARE_ALL,
+ FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT);
+ if (!NT_SUCCESS(Status)) {
+ NtClose(DirHandle);
+ m->Error = PsxStatusToErrno(Status);
+ return FALSE;
+ }
+
+ EndImpersonation();
+
+ //
+ // add the file to the directory, giving it a new name if there's
+ // already an existing file with this name.
+ //
+
+ New_U.Buffer = wcBuf;
+ New_U.MaximumLength = sizeof(wcBuf);
+
+ pRenameInfo = RtlAllocateHeap(PsxHeap, 0, sizeof(*pRenameInfo)
+ + New_U.MaximumLength + 2);
+
+ if (NULL == pRenameInfo) {
+ NtClose(DirHandle);
+ m->Error = ENOMEM;
+ return FALSE;
+ }
+
+ NtQuerySystemTime(&Time);
+ if (!RtlTimeToSecondsSince1970(&Time, &PosixTime)) {
+ PosixTime = 0;
+ }
+
+ for (;;) {
+ Status = RtlIntegerToUnicodeString(PosixTime, 16, &New_U);
+ ASSERT(NT_SUCCESS(Status));
+
+ RtlMoveMemory(pRenameInfo->FileName, New_U.Buffer,
+ New_U.Length);
+ pRenameInfo->FileNameLength = New_U.Length;
+ pRenameInfo->ReplaceIfExists = FALSE;
+ pRenameInfo->RootDirectory = DirHandle;
+
+ Status = NtSetInformationFile(FileHandle, &Iosb, pRenameInfo,
+ sizeof(*pRenameInfo) + New_U.Length,
+ FileRenameInformation);
+
+ if (NT_SUCCESS(Status)) {
+ RtlFreeHeap(PsxHeap, 0, pRenameInfo);
+ NtClose(DirHandle);
+ NtClose(FileHandle);
+
+ IoNode->Junked = TRUE;
+
+ return TRUE;
+ }
+ if (!NT_SUCCESS(Status) &&
+ STATUS_OBJECT_NAME_COLLISION != Status) {
+
+ RtlFreeHeap(PsxHeap, 0, pRenameInfo);
+ NtClose(DirHandle);
+ NtClose(FileHandle);
+ m->Error = PsxStatusToErrno(Status);
+ return FALSE;
+ }
+
+ // change the filename and start again
+
+ ++PosixTime;
+ }
+
+ // NOTREACHED
+}
diff --git a/private/posix/psxss/srvhandl.c b/private/posix/psxss/srvhandl.c
new file mode 100644
index 000000000..980ca57fc
--- /dev/null
+++ b/private/posix/psxss/srvhandl.c
@@ -0,0 +1,3303 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ srvhandl.c
+
+Abstract:
+
+ This module implements all file descriptor oriented APIs.
+
+Author:
+
+ Mark Lucovsky (markl) 30-Mar-1989
+ Ellen Aycock-Wright (ellena) Nov-1990
+
+Revision History:
+
+--*/
+
+
+#include <sys/stat.h>
+#include "psxsrv.h"
+
+#define NTPSX_ONLY
+#include "sesport.h"
+
+void
+FindOwnerModeFile(
+ IN HANDLE FileHandle,
+ OUT struct stat *StatBuf
+ );
+
+PSID
+GetLoginGroupSid(
+ IN PPSX_PROCESS p
+ );
+
+static PVOID pvInitSDMem[10];
+
+ULONG OflagToDesiredAccess[8] = {
+ FILE_READ_DATA,
+ FILE_WRITE_DATA,
+ FILE_READ_DATA | FILE_WRITE_DATA,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+};
+
+PSID MakeSid();
+static BOOLEAN IsUserInGroup(
+ PPSX_PROCESS p,
+ PSID Group
+ );
+
+BOOLEAN Open(
+ PPSX_PROCESS p,
+ PPSX_API_MSG m,
+ PUNICODE_STRING Path,
+ ULONG,
+ mode_t,
+ PULONG pRetVal,
+ PULONG Error
+ );
+
+
+BOOLEAN
+PsxOpen(
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements POSIX Open.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the open request.
+
+Return Value:
+
+ TRUE - The contents of *m should be used to generate a reply.
+
+--*/
+
+{
+ PPSX_OPEN_MSG args;
+ NTSTATUS Status;
+ BOOLEAN r;
+
+ args = &m->u.Open;
+
+ //
+ // Check validity of pathname pointer
+ //
+
+ if (!ISPOINTERVALID_CLIENT(p, args->Path_U.Buffer,
+ args->Path_U.Length)) {
+
+ KdPrint(("Invalid pointer to open: 0x%x\n",
+ args->Path_U.Buffer));
+ m->Error = EINVAL;
+ return TRUE;
+ }
+
+
+ Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
+ ASSERT(NT_SUCCESS(Status));
+
+ r = Open(p, m, &args->Path_U, args->Flags, args->Mode, &m->ReturnValue,
+ &m->Error);
+
+ EndImpersonation();
+
+ return r;
+}
+
+//
+// Internal support routine
+//
+
+BOOLEAN
+Open(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m,
+ IN PUNICODE_STRING Path,
+ IN ULONG Flags,
+ IN mode_t Mode,
+ OUT PULONG pRetVal,
+ OUT PULONG pError
+ )
+{
+ HANDLE FileHandle;
+ NTSTATUS Status;
+ PFILEDESCRIPTOR Fd;
+ ULONG FdIndex;
+ ULONG DesiredAccess;
+ IO_STATUS_BLOCK Iosb;
+ ULONG Options;
+ FILE_INTERNAL_INFORMATION SerialNumber;
+ OBJECT_ATTRIBUTES ObjA;
+ ULONG FileClass;
+ FILE_ALLOCATION_INFORMATION AllocationInfo;
+ FILE_BASIC_INFORMATION BasicInfo;
+ struct stat StatBuf;
+ ULONG PosixTime;
+
+ Fd = AllocateFd(p, 0, &FdIndex);
+ if (NULL == Fd) {
+ // No file descriptors are available.
+
+ *pError = EMFILE;
+ return TRUE;
+ }
+
+ DesiredAccess = OflagToDesiredAccess[Flags & O_ACCMODE];
+
+ //
+ // Whenever a file is opened, its ownership, protection, file type
+ // information is available. This implies that to view a file from
+ // posix, the following access rights are required:
+ //
+ // READ_CONTROL - to derive owner and mode
+ // FILE_READ_ATTRIBUTES - to read control bit to see if what we
+ // got was a pipe
+ // FILE_READ_EA - to read EA's that determine object type
+ // SYNCHRONIZE - to do syncronous IO
+ //
+
+ DesiredAccess |= (SYNCHRONIZE | READ_CONTROL | FILE_READ_ATTRIBUTES |
+ FILE_READ_EA);
+
+ Options = FILE_SYNCHRONOUS_IO_NONALERT;
+
+ //
+ // If the pathname is /dev/tty, make the fd be open
+ // on the process console.
+ //
+
+ {
+ UNICODE_STRING U;
+
+ RtlInitUnicodeString(&U, L"\\DosDevices\\X:\\dev\\tty");
+
+ if (Path->Length == U.Length &&
+ 0 == wcsncmp(&Path->Buffer[14], &U.Buffer[14], 8)) {
+
+ *pRetVal = FdIndex;
+ *pError = OpenTty(p, Fd, DesiredAccess, Flags, TRUE);
+ return TRUE;
+ }
+ }
+
+ //
+ // If the pathname is /dev/null, make the fd be open
+ // on the corresponding special device.
+ //
+
+ {
+ UNICODE_STRING U;
+
+ RtlInitUnicodeString(&U, L"\\DosDevices\\X:\\dev\\null");
+
+ if (Path->Length == U.Length &&
+ 0 == wcsncmp(&Path->Buffer[14], &U.Buffer[14], 8)) {
+
+ *pRetVal = FdIndex;
+ *pError = OpenDevNull(p, Fd, DesiredAccess, Flags);
+ return TRUE;
+ }
+ }
+
+ //
+ // We ask for delete access when we open the file, and if we
+ // can't get it we try without. If the file is unlinked while
+ // it's open, it gets moved to the junkyard, and when the last
+ // close occurs we use the delete access to remove it.
+ //
+
+ DesiredAccess |= DELETE;
+
+ if (Flags & O_CREAT) {
+ CHAR buf[SECURITY_DESCRIPTOR_MIN_LENGTH];
+ PSECURITY_DESCRIPTOR pSD = (PVOID)buf;
+
+ Status = InitSecurityDescriptor(pSD, Path, p->Process,
+ (Mode & _S_PROT) & ~p->FileModeCreationMask,
+ pvInitSDMem);
+ if (!NT_SUCCESS(Status)) {
+ *pError = PsxStatusToErrno(Status);
+ return TRUE;
+ }
+
+ InitializeObjectAttributes(&ObjA, Path, 0, NULL, pSD);
+
+ Status = NtCreateFile(&FileHandle, DesiredAccess, &ObjA, &Iosb,
+ NULL, FILE_ATTRIBUTE_NORMAL, SHARE_ALL,
+ (Flags & O_EXCL) ? FILE_CREATE : FILE_OPEN_IF,
+ Options, NULL, 0);
+
+ if (STATUS_ACCESS_DENIED == Status ||
+ STATUS_SHARING_VIOLATION == Status) {
+ DesiredAccess &= ~DELETE;
+
+ Status = NtCreateFile(&FileHandle, DesiredAccess,
+ &ObjA, &Iosb,
+ NULL, FILE_ATTRIBUTE_NORMAL, SHARE_ALL,
+ (Flags & O_EXCL) ? FILE_CREATE : FILE_OPEN_IF,
+ Options, NULL, 0);
+ }
+
+ DeInitSecurityDescriptor(pSD, pvInitSDMem);
+
+ } else {
+ InitializeObjectAttributes(&ObjA, Path, 0, NULL, NULL);
+
+ Status = NtOpenFile(&FileHandle, DesiredAccess, &ObjA, &Iosb,
+ SHARE_ALL, Options);
+
+ if (STATUS_ACCESS_DENIED == Status ||
+ STATUS_SHARING_VIOLATION == Status) {
+ DesiredAccess &= ~DELETE;
+
+ Status = NtOpenFile(&FileHandle, DesiredAccess, &ObjA, &Iosb,
+ SHARE_ALL, Options);
+ }
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ if (Status == STATUS_OBJECT_PATH_NOT_FOUND) {
+ *pError = PsxStatusToErrnoPath(Path);
+ return TRUE;
+ }
+ *pError = PsxStatusToErrno(Status);
+ return TRUE;
+ }
+
+ //
+ // Determine what type of file we have here.
+ //
+
+ FileClass = PsxDetermineFileClass(FileHandle);
+
+ if (S_IFDIR == FileClass &&
+ (DesiredAccess & FILE_WRITE_DATA)) {
+ //
+ // 1003.1-90 (5.3.1.4): the open() function shall return
+ // -1 and set errno...
+ // [EISDIR] The named file is a directory, and
+ // the oflag argument specifies write
+ // or read/write access.
+ NtClose(FileHandle);
+ *pError = EISDIR;
+ return TRUE;
+ }
+
+ if ((Flags & O_TRUNC) && FileClass != S_IFIFO &&
+ (DesiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
+
+ AllocationInfo.AllocationSize = RtlConvertLongToLargeInteger(0);
+ Status = NtSetInformationFile(FileHandle, &Iosb,
+ &AllocationInfo, sizeof(AllocationInfo), FileAllocationInformation);
+ if (!NT_SUCCESS(Status)) {
+ NtClose(FileHandle);
+ *pError = PsxStatusToErrno(Status);
+ return TRUE;
+ }
+ }
+
+ Fd->SystemOpenFileDesc = AllocateSystemOpenFile();
+ Fd->SystemOpenFileDesc->NtIoHandle = FileHandle;
+
+ if (DesiredAccess & FILE_READ_DATA) {
+ Fd->SystemOpenFileDesc->Flags |= PSX_FD_READ;
+ }
+ if (DesiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA)) {
+ Fd->SystemOpenFileDesc->Flags |= PSX_FD_WRITE;
+ }
+ if (Flags & O_NONBLOCK) {
+ Fd->SystemOpenFileDesc->Flags |= PSX_FD_NOBLOCK;
+ }
+ if (Flags & O_APPEND) {
+ Fd->SystemOpenFileDesc->Flags |= PSX_FD_APPEND;
+ }
+
+ //
+ // Get serial numbers.
+ //
+
+ Status = NtQueryInformationFile(FileHandle, &Iosb, &SerialNumber,
+ sizeof(SerialNumber), FileInternalInformation);
+
+ if (!NT_SUCCESS(Status)) {
+ NtClose(FileHandle);
+ *pError = PsxStatusToErrno(Status);
+ return TRUE;
+ }
+
+ if (ReferenceOrCreateIoNode(GetFileDeviceNumber(Path),
+ (ino_t)SerialNumber.IndexNumber.LowPart,
+ FALSE, &Fd->SystemOpenFileDesc->IoNode)) {
+
+ //
+ // Existing Node Minimal Init
+ //
+
+ RtlLeaveCriticalSection(
+ &Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
+ *pRetVal = FdIndex;
+ return IoOpenNewHandle(p, Fd, m);
+ }
+
+ //
+ // New IoNode; lots of initialization
+ //
+
+ if (FileClass == S_IFIFO) {
+ Fd->SystemOpenFileDesc->IoNode->IoVectors = &NamedPipeVectors;
+ Fd->SystemOpenFileDesc->IoNode->Context =
+ RtlAllocateHeap(PsxHeap, 0, sizeof(LOCAL_PIPE));
+ InitializeLocalPipe(
+ (PLOCAL_PIPE)Fd->SystemOpenFileDesc->IoNode->Context);
+ } else {
+ Fd->SystemOpenFileDesc->IoNode->IoVectors = &FileVectors;
+ }
+
+ Fd->SystemOpenFileDesc->IoNode->PathLength = Path->Length/2 -
+ strlen("/DosDevices/X:");
+
+ StatBuf.st_mode = FileClass;
+ FindOwnerModeFile(FileHandle, &StatBuf);
+ Fd->SystemOpenFileDesc->IoNode->Mode =
+ StatBuf.st_mode & ~(p->FileModeCreationMask);
+
+ Fd->SystemOpenFileDesc->IoNode->OwnerId = StatBuf.st_uid;
+ Fd->SystemOpenFileDesc->IoNode->GroupId = StatBuf.st_gid;
+
+ //
+ // Get the file timestamps, convert to Posix format, and stash
+ // them away in the IoNode.
+ //
+
+ Status = NtQueryInformationFile(FileHandle, &Iosb, (PVOID)&BasicInfo,
+ sizeof(BasicInfo), FileBasicInformation);
+
+ if (!NT_SUCCESS(Status)) {
+ *pError = PsxStatusToErrno(Status);
+ return TRUE;
+ }
+
+ if (!RtlTimeToSecondsSince1970(&BasicInfo.LastAccessTime, &PosixTime)) {
+ PosixTime = 0;
+ }
+ Fd->SystemOpenFileDesc->IoNode->AccessDataTime = PosixTime;
+
+ if (!RtlTimeToSecondsSince1970(&BasicInfo.LastWriteTime, &PosixTime)) {
+ PosixTime = 0;
+ }
+ Fd->SystemOpenFileDesc->IoNode->ModifyDataTime = PosixTime;
+
+ if (!RtlTimeToSecondsSince1970(&BasicInfo.ChangeTime, &PosixTime)) {
+ PosixTime = 0;
+ }
+ Fd->SystemOpenFileDesc->IoNode->ModifyIoNodeTime = PosixTime;
+
+ RtlLeaveCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
+
+ *pRetVal = FdIndex;
+
+ return IoOpenNewHandle(p, Fd, m);
+}
+
+
+BOOLEAN
+PsxClose (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements POSIX Close.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the close request.
+
+Return Value:
+
+ TRUE - The contents of *m should be used to generate a reply.
+
+--*/
+
+{
+ PPSX_CLOSE_MSG args;
+ ULONG FdIndex;
+
+ args = &m->u.Close;
+
+ FdIndex = args->FileDes;
+
+ if (!ISFILEDESINRANGE(FdIndex)) {
+ m->Error = EBADF;
+ return TRUE;
+ }
+
+ if (!DeallocateFd(p, FdIndex)) {
+
+ //
+ // Bad File Descriptor
+ //
+
+ m->Error = EBADF;
+ return TRUE;
+ }
+
+ m->ReturnValue = 0;
+ return TRUE;
+}
+
+
+BOOLEAN
+PsxPipe (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements POSIX Pipe.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the pipe request.
+
+Return Value:
+
+ TRUE - The contents of *m should be used to generate a reply.
+
+--*/
+
+{
+ PPSX_PIPE_MSG args;
+ PFILEDESCRIPTOR ReadFd,WriteFd;
+ ULONG ReadFdIndex,WriteFdIndex;
+ PLOCAL_PIPE Pipe;
+ PIONODE IoNode;
+ LARGE_INTEGER Time;
+ ULONG PosixTime;
+
+ args = &m->u.Pipe;
+
+
+ //
+ // Allocate two file descriptors. One for read, and one
+ // for write.
+ //
+
+ ReadFd = AllocateFd(p, 0, &ReadFdIndex);
+ if (NULL == ReadFd) {
+
+ //
+ // No file descriptors available
+ //
+
+ m->Error = EMFILE;
+ return TRUE;
+ }
+
+ //
+ // Set FileDesc field so next allocate wont bump in to it
+ //
+
+ ReadFd->SystemOpenFileDesc = (PSYSTEMOPENFILE)1;
+
+ WriteFd = AllocateFd(p, 0, &WriteFdIndex);
+ if (NULL == WriteFd) {
+
+ //
+ // No file descriptors available
+ //
+
+ ReadFd->SystemOpenFileDesc = (PSYSTEMOPENFILE)NULL;
+ m->Error = EMFILE;
+ return TRUE;
+ }
+
+ Pipe = RtlAllocateHeap(PsxHeap, 0, sizeof(LOCAL_PIPE));
+ if (NULL == Pipe) {
+ // XXX.mjb: deallocate fd's.
+ m->Error = ENOMEM;
+ return TRUE;
+ }
+
+ ReadFd->SystemOpenFileDesc = AllocateSystemOpenFile();
+ ReadFd->SystemOpenFileDesc->Flags = PSX_FD_READ;
+ ReadFd->SystemOpenFileDesc->NtIoHandle = (HANDLE)NULL;
+
+ WriteFd->SystemOpenFileDesc = AllocateSystemOpenFile();
+ WriteFd->SystemOpenFileDesc->Flags = PSX_FD_WRITE;
+ WriteFd->SystemOpenFileDesc->NtIoHandle = (HANDLE)NULL;
+
+ if (ReferenceOrCreateIoNode((dev_t)PSX_LOCAL_PIPE, (ino_t)Pipe,
+ FALSE, &ReadFd->SystemOpenFileDesc->IoNode)) {
+ Panic("PsxPipe: Existing IONODE ?\n");
+ }
+ ReferenceOrCreateIoNode((dev_t)PSX_LOCAL_PIPE, (ino_t)Pipe,
+ FALSE, &WriteFd->SystemOpenFileDesc->IoNode);
+
+ IoNode = ReadFd->SystemOpenFileDesc->IoNode;
+ IoNode->Context = (PVOID)Pipe;
+ IoNode->IoVectors = &LocalPipeVectors;
+ IoNode->PathLength = 0;
+
+ //
+ // Note that all the following fields are 'implementation dependent' for
+ // local pipes except the times fields.
+ //
+
+ IoNode->Mode = (mode_t)0;
+ IoNode->OwnerId = p->EffectiveUid;
+ IoNode->GroupId = p->EffectiveGid;
+
+ NtQuerySystemTime(&Time);
+ if (!RtlTimeToSecondsSince1970(&Time, &PosixTime)) {
+ PosixTime = 0L; // Time not within range of 1970 - 2105
+ }
+
+ IoNode->AccessDataTime = IoNode->ModifyDataTime = IoNode->ModifyIoNodeTime =
+ PosixTime;
+ InitializeLocalPipe(Pipe);
+
+ RtlLeaveCriticalSection(&IoNode->IoNodeLock);
+
+ //
+ // call new handle routines
+ //
+
+ IoNewHandle(p, ReadFd);
+ IoNewHandle(p, WriteFd);
+
+ args->FileDes0 = ReadFdIndex;
+ args->FileDes1 = WriteFdIndex;
+
+ m->ReturnValue = 0;
+ return TRUE;
+}
+
+
+BOOLEAN
+PsxWrite(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements POSIX write()
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the write request.
+
+Return Value:
+
+ TRUE - The contents of *m should be used to generate a reply.
+
+--*/
+
+{
+ PPSX_WRITE_MSG args;
+ PFILEDESCRIPTOR Fd;
+
+ args = &m->u.Write;
+ args->Command = IO_COMMAND_DONE;
+
+ Fd = FdIndexToFd(p,args->FileDes);
+
+ if (NULL == Fd) {
+ m->Error = EBADF;
+ return TRUE;
+ }
+
+ if (!(Fd->SystemOpenFileDesc->Flags & PSX_FD_WRITE)) {
+ m->Error = EBADF;
+ return TRUE;
+ }
+ if (0 == args->Nbytes) {
+ m->ReturnValue = 0;
+ return TRUE;
+ }
+
+ args->Scratch1 = args->Scratch2 = 0;
+ return (Fd->SystemOpenFileDesc->IoNode->IoVectors->WriteRoutine)(p, m, Fd);
+}
+
+
+BOOLEAN
+PsxRead(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements POSIX read()
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the read request.
+
+Return Value:
+
+ TRUE - The contents of *m should be used to generate a reply.
+
+--*/
+
+{
+ PPSX_READ_MSG args;
+ PFILEDESCRIPTOR Fd;
+
+ args = &m->u.Read;
+ args->Command = IO_COMMAND_DONE;
+
+ Fd = FdIndexToFd(p, args->FileDes);
+ if (NULL == Fd) {
+ m->Error = EBADF;
+ return TRUE;
+ }
+
+ if (!(Fd->SystemOpenFileDesc->Flags & PSX_FD_READ)) {
+ m->Error = EBADF;
+ return TRUE;
+ }
+ if (0 == args->Nbytes) {
+ m->ReturnValue = 0;
+ return TRUE;
+ }
+
+ args->Scratch1 = args->Scratch2 = 0;
+ return (Fd->SystemOpenFileDesc->IoNode->IoVectors->ReadRoutine)(p, m, Fd);
+}
+
+
+BOOLEAN
+PsxReadDir(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements POSIX read()
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the read request.
+
+Return Value:
+
+ TRUE - The contents of *m should be used to generate a reply.
+
+--*/
+
+{
+ PPSX_READDIR_MSG args;
+ PFILEDESCRIPTOR Fd;
+
+ args = &m->u.ReadDir;
+
+ Fd = FdIndexToFd(p, args->FileDes);
+ if (NULL == Fd) {
+ m->Error = EBADF;
+ return TRUE;
+ }
+
+ if (!(Fd->SystemOpenFileDesc->Flags & PSX_FD_READ)) {
+ m->Error = EBADF;
+ return TRUE;
+ }
+ if (0 == args->Nbytes) {
+ m->ReturnValue = 0;
+ return TRUE;
+ }
+
+ return (Fd->SystemOpenFileDesc->IoNode->IoVectors->ReadRoutine)(p, m, Fd);
+}
+
+
+BOOLEAN
+PsxDup(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements POSIX dup()
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the request.
+
+Return Value:
+
+ TRUE - The contents of *m should be used to generate a reply.
+
+--*/
+
+{
+ PPSX_DUP_MSG args;
+ PFILEDESCRIPTOR Fd, FdDup;
+ ULONG FdIndex;
+ BOOLEAN RetVal;
+
+ args = &m->u.Dup;
+
+ Fd = FdIndexToFd(p, args->FileDes);
+ if (!Fd) {
+ m->Error = EBADF;
+ return TRUE;
+ }
+
+ FdDup = AllocateFd(p, 0, &FdIndex);
+ if (!Fd) {
+
+ //
+ // No file descriptors available
+ //
+
+ m->Error = EMFILE;
+ return TRUE;
+
+ }
+ RetVal = (Fd->SystemOpenFileDesc->IoNode->IoVectors->DupRoutine)
+ (p, m, Fd, FdDup);
+ m->ReturnValue = FdIndex;
+ return RetVal;
+}
+
+
+BOOLEAN
+PsxDup2(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements POSIX dup2()
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the request.
+
+Return Value:
+
+ TRUE - The contents of *m should be used to generate a reply.
+
+--*/
+
+{
+ PPSX_DUP2_MSG args;
+ PFILEDESCRIPTOR Fd, Fd2;
+ ULONG FdIndex, FdIndex2;
+ BOOLEAN RetVal;
+
+ args = &m->u.Dup2;
+
+ //
+ // If FileDes is not valid, or FileDes2 is out of range, return EBADF
+ //
+
+ Fd = FdIndexToFd(p, args->FileDes);
+
+ FdIndex = args->FileDes;
+ FdIndex2 = args->FileDes2;
+
+ if (!Fd || !ISFILEDESINRANGE(FdIndex2)) {
+
+ m->Error = EBADF;
+ return TRUE;
+ }
+
+ //
+ // If FileDes is valid and == FileDes2, just return FileDes2
+ //
+
+ if (FdIndex == FdIndex2) {
+
+ m->ReturnValue = FdIndex2;
+ return TRUE;
+ }
+
+ //
+ // Close FileDes2 (FdIndex2)
+ //
+
+ DeallocateFd(p, FdIndex2);
+
+ //
+ // Set Fd2 to file table entry just deallocated
+ //
+
+ Fd2 = &p->ProcessFileTable[FdIndex2];
+
+ // Call Dup Routine to do the work
+
+ RetVal = (Fd->SystemOpenFileDesc->IoNode->IoVectors->DupRoutine)
+ (p, m, Fd, Fd2);
+ m->ReturnValue = FdIndex2;
+ return RetVal;
+}
+
+
+BOOLEAN
+PsxLseek(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements POSIX lseek()
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the request.
+
+Return Value:
+
+ TRUE - The contents of *m should be used to generate a reply.
+
+--*/
+
+{
+ PPSX_LSEEK_MSG args;
+ PFILEDESCRIPTOR Fd;
+
+ args = &m->u.Lseek;
+
+ Fd = FdIndexToFd(p, args->FileDes);
+
+ if (!Fd) {
+ m->Error = EBADF;
+ return TRUE;
+ }
+
+ //
+ // Lseek is not allowed on FIFOs or local pipes
+ //
+
+ if (S_ISFIFO(Fd->SystemOpenFileDesc->IoNode->Mode) ||
+ Fd->SystemOpenFileDesc->IoNode->DeviceSerialNumber == PSX_LOCAL_PIPE) {
+ m->Error = ESPIPE;
+ return TRUE;
+ }
+
+ switch (args->Whence) {
+ case SEEK_SET:
+ case SEEK_CUR:
+ case SEEK_END:
+ break;
+ default:
+ m->Error = EINVAL;
+ return TRUE;
+ }
+
+ return (Fd->SystemOpenFileDesc->IoNode->IoVectors->LseekRoutine)(p, m, Fd);
+}
+
+
+BOOLEAN
+PsxUmask(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+/*++
+
+Routine Description:
+
+ This procedure implements POSIX umask.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the umask request.
+
+Return Value:
+
+ TRUE - The contents of *m should be used to generate a reply.
+
+--*/
+{
+ PPSX_UMASK_MSG args;
+ mode_t NewMask;
+
+ args = &m->u.Umask;
+
+ // Save old creation mode mask to return
+ m->ReturnValue = (mode_t) (p->FileModeCreationMask);
+
+ // Mask off all but the permission bits in Cmask
+ NewMask = args->Cmask & _S_PROT;
+
+ // Zero old permission bits in Mode mask
+ p->FileModeCreationMask &= (!_S_PROT);
+
+ // Or in new permission bits
+ p->FileModeCreationMask |= NewMask;
+
+ return TRUE;
+}
+
+
+BOOLEAN
+PsxMkDir(
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements POSIX mkdir.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the mkdir request.
+
+Return Value:
+
+ TRUE - The contents of *m should be used to generate a reply.
+
+--*/
+
+{
+ PPSX_MKDIR_MSG args;
+ HANDLE FileHandle;
+ NTSTATUS Status;
+ IO_STATUS_BLOCK Iosb;
+ OBJECT_ATTRIBUTES ObjA;
+ UNICODE_STRING Path_U;
+ ULONG DesiredAccess;
+ CHAR buf[SECURITY_DESCRIPTOR_MIN_LENGTH];
+ PSECURITY_DESCRIPTOR pSD = (PVOID)buf;
+
+ args = &m->u.MkDir;
+
+ //
+ // Check validity of pathname pointer
+ //
+
+ if (!ISPOINTERVALID_CLIENT(p,args->Path_U.Buffer,args->Path_U.Length)) {
+ KdPrint(("Invalid pointer to mkdir %lx \n",
+ args->Path_U.Buffer));
+ m->Error = EINVAL;
+ return TRUE;
+ }
+
+ Path_U = args->Path_U;
+
+ Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = InitSecurityDescriptor(pSD, &args->Path_U, p->Process,
+ (args->Mode & _S_PROT) & ~p->FileModeCreationMask,
+ pvInitSDMem);
+
+ if (!NT_SUCCESS(Status)) {
+
+ EndImpersonation();
+
+ m->Error = PsxStatusToErrno(Status);
+ return TRUE;
+ }
+
+ InitializeObjectAttributes(&ObjA, &Path_U, 0, NULL, pSD);
+
+ DesiredAccess = WRITE_OWNER | WRITE_DAC;
+
+ Status = NtCreateFile(&FileHandle, DesiredAccess, &ObjA, &Iosb, NULL,
+ FILE_ATTRIBUTE_NORMAL, 0, FILE_CREATE,
+ FILE_DIRECTORY_FILE, NULL, 0);
+
+ EndImpersonation();
+
+ DeInitSecurityDescriptor(pSD, pvInitSDMem);
+
+ if (!NT_SUCCESS(Status)) {
+ (void)NtClose(FileHandle);
+ if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
+ m->Error = PsxStatusToErrnoPath(&Path_U);
+ return TRUE;
+ }
+ m->Error = PsxStatusToErrno(Status);
+ return TRUE;
+ }
+
+ Status = NtClose(FileHandle);
+ ASSERT(NT_SUCCESS(Status));
+
+ return TRUE;
+}
+
+
+BOOLEAN
+PsxMkFifo(
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements POSIX mkfifo.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the mkfifo request.
+
+Return Value:
+
+ TRUE - The contents of *m should be used to generate a reply.
+
+--*/
+
+{
+ PPSX_MKFIFO_MSG args;
+ HANDLE FileHandle;
+ NTSTATUS Status;
+ IO_STATUS_BLOCK Iosb;
+ OBJECT_ATTRIBUTES ObjA;
+ UNICODE_STRING Path_U;
+ ULONG DesiredAccess;
+ CHAR buf[SECURITY_DESCRIPTOR_MIN_LENGTH];
+ PSECURITY_DESCRIPTOR pSD = (PVOID)buf;
+
+ args = &m->u.MkFifo;
+
+ //
+ // Check validity of pathname pointer
+ //
+
+ if (!ISPOINTERVALID_CLIENT(p,args->Path_U.Buffer,args->Path_U.Length)) {
+ KdPrint(("Invalid pointer to mkfifo %lx\n",
+ args->Path_U.Buffer));
+ m->Error = EINVAL;
+ return TRUE;
+ }
+
+ Path_U = args->Path_U;
+
+ Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = InitSecurityDescriptor(pSD, &args->Path_U, p->Process,
+ (args->Mode & _S_PROT) & ~p->FileModeCreationMask,
+ pvInitSDMem);
+ if (!NT_SUCCESS(Status)) {
+
+ EndImpersonation();
+
+ m->Error = PsxStatusToErrno(Status);
+ return TRUE;
+ }
+
+ InitializeObjectAttributes(&ObjA, &Path_U, 0, NULL, pSD);
+
+ DesiredAccess = FILE_READ_DATA | WRITE_DAC | WRITE_OWNER;
+
+ Status = NtCreateFile(&FileHandle, DesiredAccess, &ObjA, &Iosb,
+ NULL, FILE_ATTRIBUTE_SYSTEM, 0, FILE_CREATE, 0L,
+ NULL,
+ 0);
+
+ EndImpersonation();
+
+ DeInitSecurityDescriptor(pSD, pvInitSDMem);
+
+ if (!NT_SUCCESS(Status)) {
+ if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
+ m->Error = PsxStatusToErrnoPath(&Path_U);
+ return TRUE;
+ }
+ m->Error = PsxStatusToErrno(Status);
+ return TRUE;
+ }
+
+ Status = NtClose(FileHandle);
+ ASSERT(NT_SUCCESS(Status));
+
+ return TRUE;
+}
+
+
+BOOLEAN
+PsxStat(
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements POSIX stat.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the stat request.
+
+Return Value:
+
+ TRUE - The contents of *m should be used to generate a reply.
+
+--*/
+{
+ PPSX_STAT_MSG args;
+ HANDLE FileHandle;
+ NTSTATUS Status;
+ IO_STATUS_BLOCK Iosb;
+ OBJECT_ATTRIBUTES ObjA;
+ UNICODE_STRING Path_U;
+ PIONODE IoNode;
+ FILE_INTERNAL_INFORMATION SerialNumber;
+ BOOLEAN RetVal;
+
+ args = &m->u.Stat;
+
+ //
+ // Check validity of pathname
+ //
+
+ if (!ISPOINTERVALID_CLIENT(p, args->Path_U.Buffer, args->Path_U.Length)) {
+ KdPrint(("Invalid pointer to stat %lx \n", args->Path_U.Buffer));
+ m->Error = EINVAL;
+ return TRUE;
+ }
+ if (!ISPOINTERVALID_CLIENT(p, args->StatBuf, sizeof(struct stat))) {
+ m->Error = EINVAL;
+ return TRUE;
+ }
+
+ Path_U = args->Path_U;
+ InitializeObjectAttributes(&ObjA, &Path_U, 0, NULL, NULL);
+
+ Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
+ ASSERT(NT_SUCCESS(Status));
+
+ // Open the file to obtain file handle.
+
+ Status = NtOpenFile(&FileHandle,
+ SYNCHRONIZE | READ_CONTROL | FILE_READ_ATTRIBUTES |
+ FILE_READ_EA, &ObjA, &Iosb,
+ SHARE_ALL,
+ FILE_SYNCHRONOUS_IO_NONALERT);
+
+ //
+ // Convert error code if open was not successful
+ //
+
+ EndImpersonation();
+ if (!NT_SUCCESS(Status)) {
+ if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
+ m->Error = PsxStatusToErrnoPath(&Path_U);
+ return TRUE;
+ }
+ m->Error = PsxStatusToErrno(Status);
+ return TRUE;
+ }
+
+ //
+ // Get file serial numbers...
+ //
+
+ Status = NtQueryInformationFile(FileHandle, &Iosb, &SerialNumber,
+ sizeof(SerialNumber),
+ FileInternalInformation);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: NtQueryInfoFile failed: 0x%x\n", Status));
+ }
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Find the Ionode associated with this file.
+ //
+
+ if (ReferenceOrCreateIoNode(GetFileDeviceNumber(&Path_U),
+ (ino_t)SerialNumber.IndexNumber.LowPart, TRUE, &IoNode)) {
+
+ RetVal = (IoNode->IoVectors->StatRoutine)(IoNode,
+ FileHandle, args->StatBuf, &Status);
+ RtlLeaveCriticalSection(&IoNode->IoNodeLock);
+ } else {
+ //
+ // Ionode doesn't exist. Not currently open or NonPosix file.
+ //
+
+ RetVal = (FileVectors.StatRoutine)(NULL, FileHandle,
+ args->StatBuf, &Status);
+ args->StatBuf->st_dev = GetFileDeviceNumber(&Path_U);
+ }
+
+ EndImpersonation();
+ NtClose(FileHandle);
+
+ if (!NT_SUCCESS(Status)) {
+ m->Error = PsxStatusToErrno(Status);
+ return TRUE;
+ }
+
+ return RetVal;
+}
+
+
+BOOLEAN
+PsxFStat(
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m
+ )
+/*++
+
+Routine Description:
+
+ This procedure implements POSIX fstat.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the fstat request.
+
+Return Value:
+
+ TRUE - The contents of *m should be used to generate a reply.
+
+--*/
+{
+ PPSX_FSTAT_MSG args;
+ PFILEDESCRIPTOR Fd;
+ NTSTATUS Status;
+ BOOLEAN RetVal;
+
+ args = &m->u.FStat;
+
+ if (!ISPOINTERVALID_CLIENT(p, args->StatBuf, sizeof(struct stat))) {
+ m->Error = EINVAL;
+ return TRUE;
+ }
+
+ Fd = FdIndexToFd(p, args->FileDes);
+ if (!Fd) {
+ m->Error = EBADF;
+ return TRUE;
+ }
+
+ RtlEnterCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
+ RetVal = (Fd->SystemOpenFileDesc->IoNode->IoVectors->StatRoutine)
+ (Fd->SystemOpenFileDesc->IoNode,
+ Fd->SystemOpenFileDesc->NtIoHandle,
+ args->StatBuf, &Status);
+
+ RtlLeaveCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
+
+ if (!NT_SUCCESS(Status)) {
+ m->Error = PsxStatusToErrno(Status);
+ }
+
+ return RetVal;
+}
+
+
+BOOLEAN
+PsxPathConf(
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m
+ )
+/*++
+
+Routine Description:
+
+ This procedure implements POSIX pathconf.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with
+ the pathconf request.
+
+
+Return Value:
+
+ TRUE - The contents of *m should be used to generate a reply.
+
+--*/
+{
+ PPSX_PATHCONF_MSG args;
+ HANDLE FileHandle;
+ NTSTATUS Status;
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjA;
+ UNICODE_STRING Path;
+ ULONG Length;
+ PFILE_FS_ATTRIBUTE_INFORMATION pFSInfo;
+ unsigned char buf[sizeof(FILE_FS_ATTRIBUTE_INFORMATION)+
+ 128*sizeof(WCHAR)];
+
+ args = &m->u.PathConf;
+ pFSInfo = (PFILE_FS_ATTRIBUTE_INFORMATION)buf;
+
+ //
+ // Check validity of pathname
+ //
+
+ if (!ISPOINTERVALID_CLIENT(p, args->Path.Buffer, args->Path.Length)) {
+ KdPrint(("Invalid pointer to pathconf: %lx \n", args->Path.Buffer));
+ m->Error = EINVAL;
+ return TRUE;
+ }
+
+ Path = args->Path;
+ InitializeObjectAttributes(&ObjA, &Path, 0, NULL, NULL);
+
+ Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Open the file to obtain file handle.
+ //
+
+ Status = NtOpenFile(&FileHandle,
+ SYNCHRONIZE | READ_CONTROL |
+ FILE_READ_ATTRIBUTES | FILE_READ_EA,
+ &ObjA,
+ &IoStatusBlock,
+ SHARE_ALL,
+ FILE_SYNCHRONOUS_IO_NONALERT);
+
+ EndImpersonation();
+ //
+ // Convert error code if open was not successful
+ //
+
+ if (!NT_SUCCESS(Status)) {
+ if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
+ m->Error = PsxStatusToErrnoPath(&Path);
+ return TRUE;
+ }
+ m->Error = PsxStatusToErrno(Status);
+ return TRUE;
+ }
+
+ //
+ // Figure out pathconf return value.
+ //
+
+ switch(args->Name) {
+ case _PC_LINK_MAX:
+ Status = NtQueryVolumeInformationFile(FileHandle,
+ &IoStatusBlock,
+ buf,
+ sizeof(buf),
+ FileFsAttributeInformation);
+ ASSERT(NT_SUCCESS(Status));
+ if (pFSInfo->FileSystemNameLength > (sizeof(buf)-sizeof(WCHAR))) {
+ KdPrint(("PSXSS: File System Name too long in PsxPathConf\n"));
+ m->Error = EINVAL;
+ return TRUE;
+ }
+ pFSInfo->FileSystemName[pFSInfo->FileSystemNameLength/2] = L'\0';
+ if (0 == wcscmp(L"NTFS", pFSInfo->FileSystemName) ||
+ 0 == wcscmp(L"OFS", pFSInfo->FileSystemName)) {
+ m->ReturnValue = (ULONG)LINK_MAX;
+ } else {
+ m->ReturnValue = 1;
+ }
+ break;
+
+ case _PC_NAME_MAX:
+ Status = NtQueryVolumeInformationFile(FileHandle,
+ &IoStatusBlock,
+ buf,
+ sizeof(buf),
+ FileFsAttributeInformation);
+ ASSERT(NT_SUCCESS(Status));
+
+ m->ReturnValue = pFSInfo->MaximumComponentNameLength;
+ break;
+
+ case _PC_PIPE_BUF:
+ m->ReturnValue = _POSIX_PIPE_BUF;
+ break;
+
+ case _PC_CHOWN_RESTRICTED:
+ m->ReturnValue = _POSIX_CHOWN_RESTRICTED;
+ break;
+
+ case _PC_NO_TRUNC:
+ m->ReturnValue = _POSIX_NO_TRUNC;
+ break;
+
+ case _PC_PATH_MAX:
+ m->ReturnValue = PATH_MAX;
+ break;
+
+ case _PC_MAX_CANON:
+ case _PC_MAX_INPUT:
+ case _PC_VDISABLE:
+ //
+ // No limit associated with file descriptor for these variables
+ //
+ m->Error = EINVAL;
+ break;
+
+ default:
+ m->Error = EINVAL;
+ }
+
+ Status = NtClose(FileHandle);
+ return TRUE;
+}
+
+
+BOOLEAN
+PsxFPathConf(
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m
+ )
+/*++
+
+Routine Description:
+
+ This procedure implements POSIX fpathconf.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with
+ the fpathconf request.
+
+
+Return Value:
+
+ TRUE - The contents of *m should be used to generate a reply.
+
+--*/
+{
+ PPSX_FPATHCONF_MSG args;
+ PFILEDESCRIPTOR Fd;
+ NTSTATUS Status;
+ IO_STATUS_BLOCK IoStatusBlock;
+ ULONG Length;
+ PFILE_FS_ATTRIBUTE_INFORMATION pFSInfo;
+ unsigned char buf[sizeof(FILE_FS_ATTRIBUTE_INFORMATION)+
+ 128*sizeof(WCHAR)];
+
+ args = &m->u.FPathConf;
+ pFSInfo = (PFILE_FS_ATTRIBUTE_INFORMATION)buf;
+
+ Fd = FdIndexToFd(p, args->FileDes);
+ if (!Fd) {
+ m->Error = EBADF;
+ return TRUE;
+ }
+
+ switch(args->Name) {
+ case _PC_LINK_MAX:
+
+ if (&FileVectors != Fd->SystemOpenFileDesc->IoNode->IoVectors) {
+ //
+ // The file descriptor is not open on a file.
+ //
+
+ m->Error = EINVAL;
+ return TRUE;
+ }
+
+ Status = NtQueryVolumeInformationFile(
+ Fd->SystemOpenFileDesc->NtIoHandle,
+ &IoStatusBlock,
+ buf,
+ sizeof(buf),
+ FileFsAttributeInformation);
+ ASSERT(NT_SUCCESS(Status));
+ if (pFSInfo->FileSystemNameLength > (sizeof(buf)-sizeof(WCHAR))) {
+ KdPrint(("PSXSS: File System Name too long in PsxPathConf\n"));
+ m->Error = EINVAL;
+ return TRUE;
+ }
+ pFSInfo->FileSystemName[pFSInfo->FileSystemNameLength/2] = L'\0';
+ if (0 == wcscmp(L"NTFS", pFSInfo->FileSystemName) ||
+ 0 == wcscmp(L"OFS", pFSInfo->FileSystemName)) {
+ m->ReturnValue = (ULONG)(LINK_MAX); //
+ } else {
+ m->ReturnValue = 1;
+ }
+ break;
+
+ case _PC_NAME_MAX:
+ if (&FileVectors != Fd->SystemOpenFileDesc->IoNode->IoVectors) {
+ //
+ // The file descriptor is not open on a file.
+ //
+
+ m->Error = EINVAL;
+ return TRUE;
+ }
+
+ Status = NtQueryVolumeInformationFile(
+ Fd->SystemOpenFileDesc->NtIoHandle,
+ &IoStatusBlock,
+ buf,
+ sizeof(buf),
+ FileFsAttributeInformation);
+ ASSERT(NT_SUCCESS(Status));
+
+ m->ReturnValue = pFSInfo->MaximumComponentNameLength;
+ break;
+
+ case _PC_PIPE_BUF:
+ m->ReturnValue = _POSIX_PIPE_BUF;
+ break;
+
+ case _PC_CHOWN_RESTRICTED:
+ m->ReturnValue = _POSIX_CHOWN_RESTRICTED;
+ break;
+
+ case _PC_NO_TRUNC:
+ m->ReturnValue = _POSIX_NO_TRUNC;
+ break;
+
+ case _PC_PATH_MAX:
+ m->ReturnValue = PATH_MAX;
+ break;
+
+ case _PC_MAX_CANON:
+ case _PC_MAX_INPUT:
+ case _PC_VDISABLE:
+ //
+ // No limit associated with file descriptor for these variables
+ //
+ m->Error = EINVAL;
+ break;
+
+ default:
+ m->Error = EINVAL;
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+PsxChmod(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+/*++
+
+Routine Description:
+
+ This procedure implements POSIX chmod.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the chmod request.
+
+Return Value:
+
+ TRUE - The contents of *m should be used to generate a reply.
+
+--*/
+{
+ PPSX_CHMOD_MSG args;
+ HANDLE FileHandle;
+ NTSTATUS Status;
+ IO_STATUS_BLOCK
+ Iosb;
+ OBJECT_ATTRIBUTES
+ ObjA;
+ UNICODE_STRING
+ Path_U;
+ SECURITY_INFORMATION
+ SecurityInformation;
+ PSECURITY_DESCRIPTOR
+ SecurityDescriptor = NULL;
+ PACL pDacl = NULL;
+ ULONG DaclSize; // the size of the Dacl
+ PSID NtOwner, NtGroup;
+ BOOLEAN OwnerDefaulted, GroupDefaulted;
+ BOOLEAN DaclPresent, DaclDefaulted;
+ ACCESS_MASK
+ UserAccess, GroupAccess, OtherAccess;
+ ULONG LengthNeeded,
+ Revision, // Security Desc revision
+ SdSize; // Size for Security Desc
+ SECURITY_DESCRIPTOR_CONTROL
+ Control;
+
+ // Pointers for a manufactured absolute-format security descriptor.
+
+ PACL pAbsDacl = NULL,
+ pAbsSacl = NULL;
+ PSID pAbsOwner = NULL,
+ pAbsGroup = NULL;
+
+ args = &m->u.Chmod;
+
+ if (!ISPOINTERVALID_CLIENT(p, args->Path_U.Buffer,
+ args->Path_U.Length)) {
+ KdPrint(("Invalid pointer to chmod: %lx\n", args->Path_U.Buffer));
+ m->Error = EINVAL;
+ return TRUE;
+ }
+
+ Path_U = args->Path_U;
+ InitializeObjectAttributes(&ObjA, &Path_U, 0, NULL, NULL);
+
+ Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = NtOpenFile(&FileHandle, SYNCHRONIZE | WRITE_DAC | READ_CONTROL,
+ &ObjA, &Iosb,
+ SHARE_ALL,
+ FILE_SYNCHRONOUS_IO_NONALERT);
+
+ if (!NT_SUCCESS(Status)) {
+ NTSTATUS st;
+
+ //
+ // 1003.1-90: EPERM when the euid does not match the
+ // owner of the file. We try to open the file with null
+ // access, if we can't do that then search permission is
+ // denied or somesuch. Otherwise, we give EPERM.
+ //
+
+ st = NtOpenFile(&FileHandle, SYNCHRONIZE | READ_CONTROL,
+ &ObjA, &Iosb,
+ SHARE_ALL,
+ FILE_SYNCHRONOUS_IO_NONALERT);
+ EndImpersonation();
+ if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
+ m->Error = PsxStatusToErrnoPath(&Path_U);
+ return TRUE;
+ }
+ if (!NT_SUCCESS(st)) {
+ m->Error = PsxStatusToErrno(st);
+ return TRUE;
+ }
+
+ NtClose(FileHandle);
+ m->Error = EPERM;
+ return TRUE;
+ }
+ EndImpersonation();
+
+
+ //
+ // Get the security descriptor for the file.
+ //
+
+ SecurityInformation = OWNER_SECURITY_INFORMATION |
+ GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
+
+ Status = NtQuerySecurityObject(FileHandle, SecurityInformation,
+ NULL, 0, &SdSize);
+ ASSERT(STATUS_BUFFER_TOO_SMALL == Status);
+
+ SecurityDescriptor = RtlAllocateHeap(PsxHeap, 0, SdSize);
+ if (NULL == SecurityDescriptor) {
+ m->Error = ENOMEM;
+ goto out;
+ }
+
+ Status = NtQuerySecurityObject(FileHandle, SecurityInformation,
+ SecurityDescriptor, SdSize, &SdSize);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = RtlGetControlSecurityDescriptor(SecurityDescriptor,
+ &Control, &Revision);
+ ASSERT(NT_SUCCESS(Status));
+ ASSERT(SECURITY_DESCRIPTOR_REVISION == Revision);
+
+ if (Control & SE_SELF_RELATIVE) {
+ PSECURITY_DESCRIPTOR
+ AbsSD; // SD in absolute format.
+
+ ULONG AbsSdSize = 0,
+ DaclSize = 0,
+ SaclSize = 0,
+ OwnerSize = 0,
+ GroupSize = 0;
+
+ //
+ // This security descriptor needs to be converted to absolute
+ // format.
+ //
+
+ Status = RtlSelfRelativeToAbsoluteSD(SecurityDescriptor,
+ NULL, &AbsSdSize, NULL, &DaclSize, NULL, &SaclSize,
+ NULL, &OwnerSize, NULL, &GroupSize);
+ ASSERT(STATUS_BUFFER_TOO_SMALL == Status);
+
+ AbsSD = RtlAllocateHeap(PsxHeap, 0, AbsSdSize);
+ pAbsDacl = RtlAllocateHeap(PsxHeap, 0, DaclSize);
+ pAbsSacl = RtlAllocateHeap(PsxHeap, 0, SaclSize);
+ pAbsOwner = RtlAllocateHeap(PsxHeap, 0, OwnerSize);
+ pAbsGroup = RtlAllocateHeap(PsxHeap, 0, GroupSize);
+
+ if (NULL == AbsSD || NULL == pAbsDacl || NULL == pAbsSacl
+ || NULL == pAbsOwner || NULL == pAbsGroup) {
+ m->Error = ENOMEM;
+ goto out;
+ }
+
+ Status = RtlSelfRelativeToAbsoluteSD(SecurityDescriptor,
+ AbsSD, &AbsSdSize, pAbsDacl, &DaclSize, pAbsSacl,
+ &SaclSize, pAbsOwner, &OwnerSize, pAbsGroup,
+ &GroupSize);
+ ASSERT(NT_SUCCESS(Status));
+
+ // RtlFreeHeap(PsxHeap, 0, SecurityDescriptor);
+ SecurityDescriptor = AbsSD;
+ AbsSD = NULL; // so it won't be freed later
+ }
+
+ //
+ // Get the owner and group from the security descriptor
+ //
+
+ Status = RtlGetOwnerSecurityDescriptor(SecurityDescriptor,
+ &NtOwner, &OwnerDefaulted);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = RtlGetGroupSecurityDescriptor(SecurityDescriptor,
+ &NtGroup, &GroupDefaulted);
+ ASSERT(NT_SUCCESS(Status));
+
+ if (NULL == NtOwner || NULL == NtGroup) {
+ //
+ // We need an owner and group to change the modes; fail.
+ //
+ KdPrint(("PsxChmod: No owner or no group\n"));
+ m->Error = EPERM;
+ goto out;
+ }
+
+ Status = RtlGetDaclSecurityDescriptor(SecurityDescriptor,
+ &DaclPresent, &pDacl, &DaclDefaulted);
+ ASSERT(NT_SUCCESS(Status));
+
+ ModeToAccessMask(args->Mode, &UserAccess, &GroupAccess, &OtherAccess);
+
+ Status = RtlMakePosixAcl(ACL_REVISION2, NtOwner, NtGroup, UserAccess,
+ GroupAccess, OtherAccess, 0, NULL, &LengthNeeded);
+ ASSERT(STATUS_BUFFER_TOO_SMALL == Status);
+
+ pDacl = RtlAllocateHeap(PsxHeap, 0, LengthNeeded);
+ if (NULL == pDacl) {
+ m->Error = ENOMEM;
+ return TRUE;
+ }
+
+ Status = RtlMakePosixAcl(ACL_REVISION2, NtOwner, NtGroup, UserAccess,
+ GroupAccess, OtherAccess, LengthNeeded, pDacl, &DaclSize);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = RtlSetDaclSecurityDescriptor(SecurityDescriptor, TRUE,
+ pDacl, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: RtlSetDacl: 0x%x\n", Status));
+ m->Error = EPERM;
+ return TRUE;
+ }
+
+ SecurityInformation = DACL_SECURITY_INFORMATION;
+
+ Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
+ ASSERT(NT_SUCCESS(Status));
+ {
+ Status = NtSetSecurityObject(FileHandle, SecurityInformation,
+ SecurityDescriptor);
+ } EndImpersonation();
+
+ if (!NT_SUCCESS(Status)) {
+ m->Error = PsxStatusToErrno(Status);
+ }
+
+out:
+ NtClose(FileHandle);
+
+ if (NULL != pAbsDacl) {
+ RtlFreeHeap(PsxHeap, 0, pAbsDacl);
+ }
+ if (NULL != pAbsSacl) {
+ RtlFreeHeap(PsxHeap, 0, pAbsSacl);
+ }
+ if (NULL != pAbsOwner) {
+ RtlFreeHeap(PsxHeap, 0, pAbsOwner);
+ }
+ if (NULL != pAbsGroup) {
+ RtlFreeHeap(PsxHeap, 0, pAbsGroup);
+ }
+ if (NULL != SecurityDescriptor) {
+ RtlFreeHeap(PsxHeap, 0, SecurityDescriptor);
+ }
+ if (NULL != pDacl) {
+ RtlFreeHeap(PsxHeap, 0, pDacl);
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+PsxChown(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+/*++
+
+Routine Description:
+
+ This procedure implements POSIX chown.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the chown request.
+
+Return Value:
+
+ TRUE - The contents of *m should be used to generate a reply.
+
+--*/
+{
+ PPSX_CHOWN_MSG args;
+ HANDLE FileHandle;
+ NTSTATUS Status;
+ IO_STATUS_BLOCK Iosb;
+ OBJECT_ATTRIBUTES ObjA;
+ UNICODE_STRING Path_U;
+ PIONODE IoNode;
+ SECURITY_INFORMATION SecurityInformation;
+ PSECURITY_DESCRIPTOR
+ SecurityDescriptor = NULL,
+ pFreeSD = NULL;
+ ACCESS_MASK UserAccess, GroupAccess, OtherAccess;
+ ULONG LengthNeeded;
+ PSID NtOwner, // owner from existing SD
+ NtGroup, // group from existing SD
+ DomainSid, // domain goes with new gid
+ NewGroup = NULL, // new group from DomainSid+gid
+ NewUser = NULL; // new user
+ BOOLEAN OwnerDefaulted, GroupDefaulted, DaclPresent, DaclDefaulted;
+ PACL pDacl = NULL, pDacl2 = NULL;
+ ULONG Revision; // Security Desc revision
+ SECURITY_DESCRIPTOR_CONTROL
+ Control;
+ FILE_INTERNAL_INFORMATION
+ SerialNumber;
+
+ // Pointers for a manufactured absolute-format security descriptor.
+
+ PACL pAbsDacl = NULL,
+ pAbsSacl = NULL;
+ PSID pAbsOwner = NULL,
+ pAbsGroup = NULL;
+
+ args = &m->u.Chown;
+
+ if (!ISPOINTERVALID_CLIENT(p, args->Path_U.Buffer,
+ args->Path_U.Length)) {
+ KdPrint(("Invalid pointer to chown: %lx\n",
+ args->Path_U.Buffer));
+ m->Error = EINVAL;
+ return TRUE;
+ }
+
+ Path_U = args->Path_U;
+ InitializeObjectAttributes(&ObjA, &Path_U, 0, NULL, NULL);
+
+ Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = NtOpenFile(&FileHandle,
+ SYNCHRONIZE | READ_CONTROL | WRITE_OWNER | WRITE_DAC,
+ &ObjA, &Iosb,
+ SHARE_ALL,
+ FILE_SYNCHRONOUS_IO_NONALERT);
+
+ EndImpersonation();
+
+ if (!NT_SUCCESS(Status)) {
+ //
+ // 1003.1-90 (5.6.5.4): EPERM when the caller does not have
+ // permission to change the owner. We check this by opening
+ // the file with the same mode, except without WRITE_OWNER.
+ //
+
+ if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
+ m->Error = PsxStatusToErrnoPath(&Path_U);
+ } else {
+ m->Error = PsxStatusToErrno(Status);
+ }
+
+ Status = NtImpersonateClientOfPort(p->ClientPort,
+ (PPORT_MESSAGE)m);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = NtOpenFile(&FileHandle,
+ SYNCHRONIZE | READ_CONTROL,
+ &ObjA, &Iosb,
+ SHARE_ALL,
+ FILE_SYNCHRONOUS_IO_NONALERT);
+
+ EndImpersonation();
+ if (NT_SUCCESS(Status)) {
+ m->Error = EPERM;
+ NtClose(FileHandle);
+ }
+ return TRUE;
+ }
+
+ //
+ // Get serial numbers.
+ //
+
+ Status = NtQueryInformationFile(FileHandle, &Iosb, &SerialNumber,
+ sizeof(SerialNumber), FileInternalInformation);
+ ASSERT(NT_SUCCESS(Status));
+
+ if (ReferenceOrCreateIoNode(GetFileDeviceNumber(&Path_U),
+ (ino_t)SerialNumber.IndexNumber.LowPart, TRUE, &IoNode)) {
+ // File already is open.
+ } else {
+ IoNode = NULL;
+ }
+
+ //
+ // Get SecurityInformation for the file.
+ //
+
+ SecurityInformation = OWNER_SECURITY_INFORMATION |
+ GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
+
+ Status = NtQuerySecurityObject(FileHandle, SecurityInformation,
+ NULL, 0, &LengthNeeded);
+ ASSERT(STATUS_BUFFER_TOO_SMALL == Status);
+
+ SecurityDescriptor = RtlAllocateHeap(PsxHeap, 0, LengthNeeded);
+ if (NULL == SecurityDescriptor) {
+ m->Error = ENOMEM;
+ goto out;
+ }
+
+ Status = NtQuerySecurityObject(FileHandle, SecurityInformation,
+ SecurityDescriptor, LengthNeeded, &LengthNeeded);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = RtlGetControlSecurityDescriptor(SecurityDescriptor,
+ &Control, &Revision);
+ ASSERT(NT_SUCCESS(Status));
+ ASSERT(SECURITY_DESCRIPTOR_REVISION == Revision);
+
+ if (Control & SE_SELF_RELATIVE) {
+ PSECURITY_DESCRIPTOR
+ AbsSD; // SD in absolute format.
+
+ ULONG AbsSdSize = 0,
+ DaclSize = 0, SaclSize = 0,
+ OwnerSize = 0, GroupSize = 0;
+
+ //
+ // This security descriptor needs to be converted to absolute
+ // format.
+ //
+
+ Status = RtlSelfRelativeToAbsoluteSD(SecurityDescriptor,
+ NULL, &AbsSdSize, NULL, &DaclSize, NULL, &SaclSize,
+ NULL, &OwnerSize, NULL, &GroupSize);
+ ASSERT(STATUS_BUFFER_TOO_SMALL == Status);
+
+ AbsSD = RtlAllocateHeap(PsxHeap, 0, AbsSdSize);
+ pAbsSacl = RtlAllocateHeap(PsxHeap, 0, SaclSize);
+ pAbsOwner = RtlAllocateHeap(PsxHeap, 0, OwnerSize);
+ pAbsGroup = RtlAllocateHeap(PsxHeap, 0, GroupSize);
+ pAbsDacl = RtlAllocateHeap(PsxHeap, 0, DaclSize);
+
+ if (NULL == AbsSD || NULL == pAbsDacl || NULL == pAbsSacl
+ || NULL == pAbsOwner || NULL == pAbsGroup) {
+ m->Error = ENOMEM;
+ goto out;
+ }
+
+ Status = RtlSelfRelativeToAbsoluteSD(SecurityDescriptor,
+ AbsSD, &AbsSdSize, pAbsDacl, &DaclSize, pAbsSacl,
+ &SaclSize, pAbsOwner, &OwnerSize, pAbsGroup,
+ &GroupSize);
+ ASSERT(NT_SUCCESS(Status));
+
+ pFreeSD = SecurityDescriptor;
+ SecurityDescriptor = AbsSD;
+ }
+
+ //
+ // Get the owner and group from the security descriptor
+ //
+
+ Status = RtlGetOwnerSecurityDescriptor(SecurityDescriptor,
+ &NtOwner, &OwnerDefaulted);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = RtlGetGroupSecurityDescriptor(SecurityDescriptor,
+ &NtGroup, &GroupDefaulted);
+ ASSERT(NT_SUCCESS(Status));
+
+ if (NULL == NtOwner || NULL == NtGroup) {
+ //
+ // Seems like this file doesn't have an owner or a
+ // group, which means that we can't change it's group.
+ //
+ // XXX.mjb: ideally, would make the file owned by us if
+ // possible.
+ //
+ KdPrint(("PSXSS: PsxChown: no owner or no group\n"));
+ m->Error = EPERM;
+ goto out;
+ }
+
+ Status = RtlGetDaclSecurityDescriptor(SecurityDescriptor,
+ &DaclPresent, &pDacl, &DaclDefaulted);
+ ASSERT(NT_SUCCESS(Status));
+
+ if (!DaclPresent || (DaclPresent && NULL == pDacl)) {
+
+ //
+ // All access is allowed to this file. We attach a Posix-
+ // type acl that allows all access.
+ //
+
+ ModeToAccessMask(0777, &UserAccess, &GroupAccess, &OtherAccess);
+
+ } else {
+ Status = RtlInterpretPosixAcl(ACL_REVISION2, NtOwner, NtGroup,
+ pDacl, &UserAccess, &GroupAccess, &OtherAccess);
+ if (STATUS_COULD_NOT_INTERPRET == Status) {
+ //
+ // There is an acl, but it is not in the Posix form.
+ // Ideally, we'd like to leave the resulting file
+ // with an ACL approximating the one we've foud there,
+ // but that's hard so we'll just do the easy thing.
+ //
+
+ ModeToAccessMask(0777, &UserAccess, &GroupAccess,
+ &OtherAccess);
+ } else if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: RtlInterpretPosixAcl: 0x%x\n", Status));
+ m->Error = EPERM;
+ goto out;
+ }
+ }
+
+ if (0xFFF == args->Group) {
+ //
+ // The login group is treated specially here.
+ //
+
+ NewGroup = GetLoginGroupSid(p);
+ if (NULL == NewGroup) {
+ m->Error = ENOMEM;
+ goto out;
+ }
+ } else {
+
+ DomainSid = GetSidByOffset(args->Group & 0xFFFF0000);
+ if (NULL == DomainSid) {
+
+ //
+ // Either we can't get to the domain, or the user
+ // specified an invalid group. Bad.
+ //
+ m->Error = EINVAL;
+ goto out;
+ }
+
+ NewGroup = MakeSid(DomainSid, args->Group & 0xFFFF);
+ if (NULL == NewGroup) {
+ m->Error = ENOMEM;
+ goto out;
+ }
+ }
+
+ DomainSid = GetSidByOffset(args->Owner & 0xFFFF0000);
+ if (NULL == DomainSid) {
+ m->Error = EINVAL;
+ goto out;
+ }
+
+ NewUser = MakeSid(DomainSid, args->Owner & 0xFFFF);
+ if (NULL == NewUser) {
+ m->Error = ENOMEM;
+ goto out;
+ }
+
+ Status = RtlMakePosixAcl(ACL_REVISION2, NewUser, NewGroup,
+ UserAccess, GroupAccess, OtherAccess, 0, NULL, &LengthNeeded);
+ ASSERT(STATUS_BUFFER_TOO_SMALL == Status);
+
+ pDacl2 = RtlAllocateHeap(PsxHeap, 0, LengthNeeded);
+ if (NULL == pDacl2) {
+ m->Error = ENOMEM;
+ goto out;
+ }
+
+ Status = RtlMakePosixAcl(ACL_REVISION2, NewUser, NewGroup,
+ UserAccess, GroupAccess, OtherAccess, LengthNeeded, pDacl2,
+ &LengthNeeded);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = RtlSetDaclSecurityDescriptor(SecurityDescriptor, TRUE,
+ pDacl2, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: PsxChown: RtlSetDacl: 0x%x\n", Status));
+ m->Error = EPERM;
+ goto out;
+ }
+
+ Status = RtlSetGroupSecurityDescriptor(SecurityDescriptor, NewGroup, FALSE);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = RtlSetOwnerSecurityDescriptor(SecurityDescriptor, NewUser, FALSE);
+
+ Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
+ ASSERT(NT_SUCCESS(Status));
+ {
+ Status = NtSetSecurityObject(FileHandle, SecurityInformation,
+ SecurityDescriptor);
+ } EndImpersonation();
+
+ if (!NT_SUCCESS(Status)) {
+ m->Error = PsxStatusToErrno(Status);
+ goto out;
+ }
+
+ if (NULL != IoNode) {
+ IoNode->GroupId = args->Group;
+ }
+
+out:
+ NtClose(FileHandle);
+
+ if (NULL != SecurityDescriptor) {
+ RtlFreeHeap(PsxHeap, 0, SecurityDescriptor);
+ }
+ if (NULL != NewGroup) {
+ RtlFreeHeap(PsxHeap, 0, NewGroup);
+ }
+ if (NULL != NewUser) {
+ RtlFreeHeap(PsxHeap, 0, NewUser);
+ }
+ if (NULL != pDacl2) {
+ RtlFreeHeap(PsxHeap, 0, pDacl2);
+ }
+ if (NULL != pAbsDacl) {
+ RtlFreeHeap(PsxHeap, 0, pAbsDacl);
+ }
+ if (NULL != pAbsSacl) {
+ RtlFreeHeap(PsxHeap, 0, pAbsSacl);
+ }
+ if (NULL != pAbsOwner) {
+ RtlFreeHeap(PsxHeap, 0, pAbsOwner);
+ }
+ if (NULL != pAbsGroup) {
+ RtlFreeHeap(PsxHeap, 0, pAbsGroup);
+ }
+ if (NULL != pFreeSD) {
+ RtlFreeHeap(PsxHeap, 0, pFreeSD);
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+PsxUtime(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+/*++
+
+Routine Description:
+
+ This procedure implements POSIX utime.
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the utime request.
+
+Return Value:
+
+ TRUE - The contents of *m should be used to generate a reply.
+
+--*/
+{
+ PPSX_UTIME_MSG args;
+ HANDLE FileHandle;
+ NTSTATUS Status;
+ IO_STATUS_BLOCK Iosb;
+ OBJECT_ATTRIBUTES ObjA;
+ UNICODE_STRING Path_U;
+ FILE_BASIC_INFORMATION BasicInfo;
+ LARGE_INTEGER Time;
+
+ args = &m->u.Utime;
+
+ if (!ISPOINTERVALID_CLIENT(p,args->Path_U.Buffer,args->Path_U.Length)) {
+ KdPrint(("Invalid pointer to utime %lx\n",
+ args->Path_U.Buffer));
+ m->Error = EINVAL;
+ return TRUE;
+ }
+
+ Path_U = args->Path_U;
+
+ InitializeObjectAttributes(&ObjA, &Path_U, 0, NULL, NULL);
+
+ Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = NtOpenFile(&FileHandle,
+ SYNCHRONIZE | READ_CONTROL | FILE_WRITE_ATTRIBUTES,
+ &ObjA, &Iosb, SHARE_ALL, FILE_SYNCHRONOUS_IO_NONALERT);
+
+ if (!NT_SUCCESS(Status)) {
+ EndImpersonation();
+ if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
+ m->Error = PsxStatusToErrnoPath(&Path_U);
+ return TRUE;
+ }
+ m->Error = PsxStatusToErrno(Status);
+ return TRUE;
+ }
+
+ //
+ // 1003.1-1990: EPERM when the /times/ argument is not
+ // NULL, the calling process has write access to the file,
+ // but is not the owner of the file. Here we're denying
+ // access to the file that NT would grant.
+ //
+
+ if (NULL != args->TimesSpecified) {
+ struct stat StatBuf;
+
+ FindOwnerModeFile(FileHandle, &StatBuf);
+
+ if (StatBuf.st_uid != p->EffectiveUid) {
+ UCHAR buf[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) +
+ 128 * sizeof(WCHAR)];
+ PFILE_FS_ATTRIBUTE_INFORMATION pFSInfo = (PVOID)buf;
+
+ // We ignore this test for all but NTFS
+
+ Status = NtQueryVolumeInformationFile(FileHandle,
+ &Iosb, buf, sizeof(buf),
+ FileFsAttributeInformation);
+ ASSERT(NT_SUCCESS(Status));
+
+ pFSInfo->FileSystemName[pFSInfo->FileSystemNameLength/2] = 0;
+ if (0 == wcscmp(L"NTFS", pFSInfo->FileSystemName) ||
+ 0 == wcscmp(L"OFS", pFSInfo->FileSystemName)) {
+
+ NtClose(FileHandle);
+ m->Error = EPERM;
+ EndImpersonation();
+ return TRUE;
+ }
+ }
+ }
+
+ EndImpersonation();
+
+ RtlZeroMemory(&Time, sizeof(Time));
+ BasicInfo.CreationTime = Time;
+ BasicInfo.ChangeTime = Time;
+ BasicInfo.FileAttributes = 0;
+
+ //
+ // If the utimbuf is NULL, we're to set the file times to the current
+ // time. The owner and anyone else with write permission on the file
+ // should be able to perform this operation. If times are specified
+ // via a utimbuf, only the owner should be able to perform the
+ // operation.
+ //
+
+ if (args->TimesSpecified == NULL) {
+ NtQuerySystemTime(&Time);
+
+ BasicInfo.LastAccessTime = Time;
+ BasicInfo.LastWriteTime = Time;
+ } else {
+ RtlSecondsSince1970ToTime((ULONG)(args->Times.actime), &Time);
+ BasicInfo.LastAccessTime = Time;
+
+ RtlSecondsSince1970ToTime((ULONG)(args->Times.modtime), &Time);
+ BasicInfo.LastWriteTime = Time;
+ }
+
+ Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = NtSetInformationFile(FileHandle, &Iosb, &BasicInfo,
+ sizeof(BasicInfo), FileBasicInformation);
+
+ if (!NT_SUCCESS(Status)) {
+ m->Error = PsxStatusToErrno(Status);
+ //FALL OUT
+ }
+ EndImpersonation();
+
+ NtClose(FileHandle);
+
+ return TRUE;
+}
+
+BOOLEAN
+PsxFcntl(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements POSIX fcntl().
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the request.
+
+Return Value:
+
+ TRUE - The contents of *m should be used to generate a reply.
+ FALSE - No reply should be sent, for the case in which the process
+ was blocked (as in F_SETLKW).
+
+--*/
+
+{
+ PPSX_FCNTL_MSG args;
+ PFILEDESCRIPTOR pFd, pFdDup;
+ NTSTATUS Status;
+ BOOLEAN b;
+ int error;
+
+ args = &m->u.Fcntl;
+
+ pFd = FdIndexToFd(p, args->FileDes);
+ if (NULL == pFd) {
+ m->Error = EBADF;
+ return TRUE;
+ }
+
+ switch (args->Command) {
+ case F_DUPFD:
+ if (!ISFILEDESINRANGE(args->u.i)) {
+ m->Error = EINVAL;
+ return TRUE;
+ }
+ pFdDup = AllocateFd(p, args->u.i, &m->ReturnValue);
+ if (NULL == pFdDup) {
+ // no descriptors are available
+ m->Error = EMFILE;
+ return TRUE;
+ }
+ ASSERT(NULL != pFd->SystemOpenFileDesc->IoNode->IoVectors->DupRoutine);
+ return (pFd->SystemOpenFileDesc->IoNode->IoVectors->DupRoutine)
+ (p, m, pFd, pFdDup);
+ case F_GETFD:
+ //
+ // File descriptor flags
+ //
+
+ m->ReturnValue = 0;
+ if (pFd->Flags & PSX_FD_CLOSE_ON_EXEC) {
+ m->ReturnValue |= FD_CLOEXEC;
+ }
+ return TRUE;
+ case F_SETFD:
+ pFd->Flags = 0;
+ if (args->u.i & FD_CLOEXEC) {
+ pFd->Flags |= PSX_FD_CLOSE_ON_EXEC;
+ }
+ m->ReturnValue = 0;
+ return TRUE;
+
+ case F_GETFL:
+ //
+ // Get file description flags
+ //
+ if ((pFd->SystemOpenFileDesc->Flags &
+ (PSX_FD_READ | PSX_FD_WRITE))
+ == (PSX_FD_READ | PSX_FD_WRITE)) {
+ m->ReturnValue |= O_RDWR;
+ } else if (pFd->SystemOpenFileDesc->Flags & PSX_FD_READ) {
+ m->ReturnValue |= O_RDONLY;
+ } else if (pFd->SystemOpenFileDesc->Flags & PSX_FD_WRITE) {
+ m->ReturnValue |= O_WRONLY;
+ }
+ if (pFd->SystemOpenFileDesc->Flags & PSX_FD_NOBLOCK) {
+ m->ReturnValue |= O_NONBLOCK;
+ }
+ if (pFd->SystemOpenFileDesc->Flags & PSX_FD_APPEND) {
+ m->ReturnValue |= O_APPEND;
+ }
+ return TRUE;
+ case F_SETFL:
+ pFd->SystemOpenFileDesc->Flags &= ~(PSX_FD_APPEND | PSX_FD_NOBLOCK);
+ if (args->u.i & O_APPEND) {
+ pFd->SystemOpenFileDesc->Flags |= PSX_FD_APPEND;
+ }
+ if (args->u.i & O_NONBLOCK) {
+ pFd->SystemOpenFileDesc->Flags |= PSX_FD_NOBLOCK;
+ }
+ m->ReturnValue = 0;
+ return TRUE;
+
+ case F_SETLK:
+ case F_SETLKW:
+ //
+ // 1003.1-90 (6.5.2.4): EBADF if F_SETLK or F_SETLKW and
+ // l_type is F_RDLCK and fildes is not ... open for reading.
+ //
+
+ if (F_RDLCK == args->u.pf->l_type &&
+ !(pFd->SystemOpenFileDesc->Flags & PSX_FD_READ)) {
+ m->Error = EBADF;
+ m->ReturnValue = (ULONG)(-1);
+ return TRUE;
+ }
+
+ //
+ // ... EBADF if F_SETLK or F_SETLKW and l_type is F_WRLCK
+ // and fildes is not ... open for writing.
+ //
+
+ if (F_WRLCK == args->u.pf->l_type &&
+ !(pFd->SystemOpenFileDesc->Flags & PSX_FD_WRITE)) {
+ m->Error = EBADF;
+ m->ReturnValue = (ULONG)(-1);
+ return TRUE;
+ }
+
+ //FALLTHROUGH
+
+ case F_GETLK:
+
+ if (!(pFd->SystemOpenFileDesc->IoNode->Mode & S_IFREG)) {
+ //
+ // flocks only work on regular files
+ //
+
+ m->Error = EINVAL;
+ m->ReturnValue = (ULONG)(-1);
+ }
+
+ RtlEnterCriticalSection(&pFd->SystemOpenFileDesc->IoNode->IoNodeLock);
+
+ b = DoFlockStuff(p, m, args->Command, pFd,
+ args->u.pf, &error);
+
+ if (b) {
+ RtlLeaveCriticalSection(&pFd->SystemOpenFileDesc->IoNode->IoNodeLock);
+ }
+
+ m->Error = error;
+ if (error == 0) {
+ m->ReturnValue = 0;
+ } else {
+ m->ReturnValue = (ULONG)(-1);
+ }
+ //
+ // DoFlockStuff returns FALSE if the process was
+ // blocked and no reply should be sent.
+ //
+ return b;
+#if DBG
+ case 99:
+ DumpFlockList(pFd->SystemOpenFileDesc->IoNode);
+ break;
+#endif
+ default:
+ // This shouldn't happen; the client checks for valid
+ // command arguments.
+
+ ASSERT(0);
+ }
+ return TRUE;
+}
+
+BOOLEAN
+PsxIsatty(
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m
+ )
+{
+ PPSX_ISATTY_MSG args;
+ NTSTATUS Status;
+ PFILEDESCRIPTOR Fd;
+
+ args = &m->u.Isatty;
+
+ Fd = FdIndexToFd(p, args->FileDes);
+ if (!Fd) {
+ m->Error = EBADF;
+ return TRUE;
+ }
+
+ if (&ConVectors == Fd->SystemOpenFileDesc->IoNode->IoVectors) {
+ //
+ // If the fd is open on the console, it's not
+ // necessarily a tty, since the console session manager
+ // could have redirected the output. The dll will send
+ // a message to posix.exe, asking whether it's redirected
+ // or not.
+ //
+ args->Command = IO_COMMAND_DO_CONSIO;
+ args->FileDes = (int)Fd->SystemOpenFileDesc->NtIoHandle;
+ return TRUE;
+ }
+ args->Command = IO_COMMAND_DONE;
+
+ m->ReturnValue = FALSE; // not a tty
+ return TRUE;
+}
+
+BOOLEAN
+PsxFtruncate(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure implements POSIX ftruncate().
+
+Arguments:
+
+ p - Supplies the address of the process making the call.
+
+ m - Supplies the address of the message associated with the request.
+
+Return Value:
+
+ TRUE - The contents of *m should be used to generate a reply.
+
+--*/
+
+{
+ PPSX_FTRUNCATE_MSG args;
+ PFILEDESCRIPTOR Fd;
+ FILE_END_OF_FILE_INFORMATION EofInfo;
+ NTSTATUS Status;
+ IO_STATUS_BLOCK iosb;
+
+ args = &m->u.Ftruncate;
+
+ Fd = FdIndexToFd(p, args->FileDes);
+
+ if (!Fd) {
+ m->Error = EBADF;
+ return TRUE;
+ }
+
+ //
+ // Ftruncate is only allowed on regular files.
+ //
+
+ if (S_ISFIFO(Fd->SystemOpenFileDesc->IoNode->Mode) ||
+ Fd->SystemOpenFileDesc->IoNode->DeviceSerialNumber == PSX_LOCAL_PIPE) {
+ m->Error = ESPIPE;
+ return TRUE;
+ }
+
+ if (&FileVectors != Fd->SystemOpenFileDesc->IoNode->IoVectors) {
+ m->Error = EBADF;
+ return TRUE;
+ }
+
+ EofInfo.EndOfFile.QuadPart = args->Length;
+
+ Status = NtSetInformationFile(
+ Fd->SystemOpenFileDesc->NtIoHandle,
+ &iosb,
+ (PVOID)&EofInfo,
+ sizeof(EofInfo),
+ FileEndOfFileInformation
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ m->Error = PsxStatusToErrno(Status);
+ }
+
+ return TRUE;
+}
+
+
+//
+// Internal support routine
+//
+// InitSecurityDescriptor -- to be called when a new file is created. Sets
+// up the security descriptor appropriately.
+//
+// The filename is used only to get the name of the parent direcotory,
+// which we use to set the group in the security descriptor.
+//
+
+NTSTATUS
+InitSecurityDescriptor(
+ PSECURITY_DESCRIPTOR pSD,
+ PUNICODE_STRING pFileName,
+ IN HANDLE Process,
+ IN mode_t Mode,
+ OUT PVOID *pvMem
+ )
+{
+ PSID_AND_ATTRIBUTES
+ pSA;
+ HANDLE TokenHandle;
+ ULONG Length, LengthNeeded;
+ NTSTATUS
+ Status;
+ PACL pDacl;
+ ACCESS_MASK
+ UserAccess,
+ GroupAccess,
+ OtherAccess;
+ ANSI_STRING File_A;
+ UNICODE_STRING ParentDir_U;
+ OBJECT_ATTRIBUTES Obj;
+ HANDLE DirHandle;
+ PSID DirGroup;
+ PCHAR pch;
+ IO_STATUS_BLOCK Iosb;
+ SECURITY_INFORMATION SecurityInformation;
+ PSECURITY_DESCRIPTOR pDirSD = NULL;
+ PTOKEN_PRIMARY_GROUP pGroup = NULL;
+ BOOLEAN Defaulted;
+ int i;
+
+ Status = RtlUnicodeStringToAnsiString(&File_A, pFileName, TRUE);
+ if (!NT_SUCCESS(Status)) {
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Open the parent directory and get its group owner.
+ //
+
+ pch = strrchr(File_A.Buffer, '\\') + 1;
+ if (pch == NULL)
+ return STATUS_OBJECT_PATH_SYNTAX_BAD;
+ *pch = '\0';
+ File_A.Length = strlen(File_A.Buffer);
+
+ Status = RtlAnsiStringToUnicodeString(&ParentDir_U, &File_A, TRUE);
+ RtlFreeAnsiString(&File_A);
+ if (!NT_SUCCESS(Status)) {
+ return STATUS_NO_MEMORY;
+ }
+
+ InitializeObjectAttributes(&Obj, &ParentDir_U, 0, NULL, NULL);
+ Status = NtOpenFile(&DirHandle,
+ SYNCHRONIZE | READ_CONTROL | FILE_READ_ATTRIBUTES |
+ FILE_READ_EA, &Obj, &Iosb,
+ SHARE_ALL,
+ FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
+
+ RtlFreeUnicodeString(&ParentDir_U);
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ Status = NtOpenProcessToken(Process, GENERIC_READ, &TokenHandle);
+ if (!NT_SUCCESS(Status)) {
+ NtClose(DirHandle);
+ return Status;
+ }
+
+ SecurityInformation = GROUP_SECURITY_INFORMATION;
+
+ Status = NtQuerySecurityObject(DirHandle, SecurityInformation,
+ NULL, 0, &LengthNeeded);
+
+ if (STATUS_INVALID_PARAMETER == Status) {
+ //
+ // Can't get the group from parent dir, use primary group
+ // instead.
+ //
+ Status = NtQueryInformationToken(TokenHandle, TokenPrimaryGroup,
+ NULL, 0, &LengthNeeded);
+ ASSERT(STATUS_BUFFER_TOO_SMALL == Status);
+
+ pGroup = RtlAllocateHeap(PsxHeap, 0, LengthNeeded);
+ if (NULL == pGroup) {
+ NtClose(TokenHandle);
+ return STATUS_NO_MEMORY;
+ }
+
+ Status = NtQueryInformationToken(TokenHandle, TokenPrimaryGroup,
+ pGroup, LengthNeeded, &LengthNeeded);
+ ASSERT(NT_SUCCESS(Status));
+
+ DirGroup = pGroup->PrimaryGroup;
+
+ } else if (!NT_SUCCESS(Status)) {
+
+ static int _status;
+
+ _status = Status;
+
+ if (STATUS_BUFFER_TOO_SMALL != _status || 0 == LengthNeeded) {
+ KdPrint(("PSXSS: NtQSObject returned unexpected %x, %x\n",
+ _status, LengthNeeded));
+ return _status;
+ }
+
+ ASSERT(STATUS_BUFFER_TOO_SMALL == _status);
+
+ pDirSD = RtlAllocateHeap(PsxHeap, 0, LengthNeeded);
+ if (NULL == pDirSD) {
+ NtClose(DirHandle);
+ return STATUS_NO_MEMORY;
+ }
+
+ Status = NtQuerySecurityObject(DirHandle, SecurityInformation,
+ pDirSD, LengthNeeded, &Length);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: NtQSD: 0x%x\n", Status));
+ KdPrint(("PSXSS: NtQSD: %d vs. %d\n", LengthNeeded, Length));
+ return Status;
+ }
+
+ NtClose(DirHandle);
+
+ Status = RtlGetGroupSecurityDescriptor(pDirSD, &DirGroup,
+ &Defaulted);
+ ASSERT(NT_SUCCESS(Status));
+ }
+
+ Status = RtlCreateSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = NtQueryInformationToken(TokenHandle, TokenUser, NULL,
+ 0, &LengthNeeded);
+ ASSERT(STATUS_BUFFER_TOO_SMALL == Status);
+
+ pSA = RtlAllocateHeap(PsxHeap, 0, LengthNeeded);
+ if (NULL == pSA) {
+ NtClose(TokenHandle);
+ return STATUS_NO_MEMORY;
+ }
+
+ Status = NtQueryInformationToken(TokenHandle, TokenUser, pSA,
+ LengthNeeded, &LengthNeeded);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: NtQueryInfoToken: 0x%x\n", Status));
+ }
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = RtlSetOwnerSecurityDescriptor(pSD, pSA->Sid, FALSE);
+ ASSERT(NT_SUCCESS(Status));
+
+
+ Status = RtlSetGroupSecurityDescriptor(pSD, DirGroup, FALSE);
+ ASSERT(NT_SUCCESS(Status));
+
+ ModeToAccessMask(Mode, &UserAccess, &GroupAccess, &OtherAccess);
+
+ Status = RtlMakePosixAcl(ACL_REVISION2, pSA->Sid, DirGroup,
+ UserAccess, GroupAccess, OtherAccess, 0, NULL, &LengthNeeded);
+ ASSERT(STATUS_BUFFER_TOO_SMALL == Status);
+
+ pDacl = RtlAllocateHeap(PsxHeap, 0, LengthNeeded);
+ if (NULL == pDacl) {
+ // XXX.mjb: better cleanup needed
+ NtClose(TokenHandle);
+ return STATUS_NO_MEMORY;
+ }
+
+ Status = RtlMakePosixAcl(ACL_REVISION2, pSA->Sid, DirGroup,
+ UserAccess, GroupAccess, OtherAccess, LengthNeeded, pDacl,
+ &LengthNeeded);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = RtlSetDaclSecurityDescriptor(pSD,
+ TRUE, pDacl, FALSE);
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // The pointers we stick into pvMem will be freed in DeInitSD.
+ //
+
+ i = 0;
+
+ pvMem[i++] = pDacl;
+ pvMem[i++] = pSA;
+ if (NULL != pDirSD) {
+ pvMem[i++] = pDirSD;
+ }
+ if (NULL != pGroup) {
+ pvMem[i++] = pGroup;
+ }
+ pvMem[i++] = NULL;
+
+ NtClose(TokenHandle);
+
+ ASSERT(RtlValidSecurityDescriptor(pSD));
+
+ return STATUS_SUCCESS;
+}
+
+//
+// Internal support routine
+//
+// This routine should only be called on SD's initialized with
+// InitSecurityDescriptor(). It frees memory that was allocated
+// there.
+//
+
+VOID
+DeInitSecurityDescriptor(
+ PSECURITY_DESCRIPTOR pSD,
+ PVOID *pvMem
+ )
+{
+ PACL pDacl;
+ NTSTATUS Status;
+ BOOLEAN b;
+ int i;
+
+ for (i = 0; NULL != pvMem[i]; ++i)
+ RtlFreeHeap(PsxHeap, 0, pvMem[i]);
+
+#if 0
+ Status = RtlGetDaclSecurityDescriptor(pSD, &b, &pDacl, &b);
+ ASSERT(NT_SUCCESS(Status));
+ ASSERT(NULL != pDacl);
+
+ RtlFreeHeap(PsxHeap, 0, pDacl);
+#endif
+}
+
+//
+// Internal support routine
+//
+// See if the given group is one that the owner of this process belongs
+// to.
+//
+
+static BOOLEAN
+IsUserInGroup(
+ PPSX_PROCESS p,
+ PSID Group
+ )
+{
+ HANDLE TokenHandle;
+ TOKEN_GROUPS *pGroups;
+ ULONG LengthNeeded;
+ NTSTATUS Status;
+ BOOLEAN RetVal = FALSE;
+ ULONG i;
+
+ Status = NtOpenProcessToken(p->Process, GENERIC_READ, &TokenHandle);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = NtQueryInformationToken(TokenHandle, TokenGroups, NULL,
+ 0, &LengthNeeded);
+ ASSERT(STATUS_BUFFER_TOO_SMALL == Status);
+
+ pGroups = RtlAllocateHeap(PsxHeap, 0, LengthNeeded);
+ if (NULL == pGroups) {
+ NtClose(TokenHandle);
+ return FALSE;
+ }
+
+ Status = NtQueryInformationToken(TokenHandle, TokenGroups, pGroups,
+ LengthNeeded, &LengthNeeded);
+ ASSERT(NT_SUCCESS(Status));
+
+ for (i = 0; i < pGroups->GroupCount; ++i) {
+ if (RtlEqualSid(pGroups->Groups[i].Sid, Group)) {
+ RetVal = TRUE;
+ break;
+ }
+ }
+
+ RtlFreeHeap(PsxHeap, 0, pGroups);
+ NtClose(TokenHandle);
+ return RetVal;
+}
+
+
+//
+// Internal support routine
+//
+
+PSID
+GetLoginGroupSid(
+ IN PPSX_PROCESS p
+ )
+/*++
+
+ Get the logon group Sid from the supplementary group list. The
+ returned Sid is allocated from PsxHeap, and should be freed by
+ the caller when he's done with it.
+
+ Returns NULL upon failure.
+
+--*/
+{
+ HANDLE TokenHandle;
+ NTSTATUS Status;
+ PSID NewSid;
+ ULONG outlen, i;
+ TOKEN_GROUPS *pGroups;
+
+ Status = NtOpenProcessToken(p->Process, GENERIC_READ,
+ &TokenHandle);
+ if (!NT_SUCCESS(Status)) {
+ return NULL;
+ }
+
+ Status = NtQueryInformationToken(TokenHandle, TokenGroups,
+ NULL, 0, &outlen);
+ ASSERT(STATUS_BUFFER_TOO_SMALL == Status);
+
+ pGroups = RtlAllocateHeap(PsxHeap, 0, outlen);
+ if (NULL == pGroups) {
+ NtClose(TokenHandle);
+ return NULL;
+ }
+
+ Status = NtQueryInformationToken(TokenHandle, TokenGroups,
+ pGroups, outlen, &outlen);
+ if (!NT_SUCCESS(Status)) {
+ RtlFreeHeap(PsxHeap, 0, pGroups);
+ NtClose(TokenHandle);
+ return NULL;
+ }
+
+ for (i = 0; i < pGroups->GroupCount; ++i) {
+ PSID Sid = pGroups->Groups[i].Sid;
+
+ if (0xFFF == MakePosixId(Sid)) {
+ // This is the login group sid, copy it and return.
+
+ NewSid = RtlAllocateHeap(PsxHeap, 0,
+ RtlLengthSid(Sid));
+ if (NULL == NewSid)
+ return NULL;
+
+ Status = RtlCopySid(RtlLengthSid(Sid), NewSid, Sid);
+ ASSERT(NT_SUCCESS(Status));
+
+ RtlFreeHeap(PsxHeap, 0, pGroups);
+ NtClose(TokenHandle);
+ return NewSid;
+ }
+ }
+ RtlFreeHeap(PsxHeap, 0, pGroups);
+ NtClose(TokenHandle);
+ return NULL;
+}
+
+//
+// Internal support routine
+//
+
+ULONG
+OpenTty(
+ PPSX_PROCESS p,
+ PFILEDESCRIPTOR Fd,
+ ULONG DesiredAccess,
+ ULONG Flags,
+ BOOLEAN NewOpen
+ )
+/*++
+
+Routine Description:
+
+ This routine is called to implement an open on the file /dev/tty.
+
+Arguments:
+
+ p - The process on whose behalf the open is performed.
+ Fd - The file descriptor to receive the handle.
+ DesiredAccess - The access requested on the file.
+ Flags -
+ NewOpen - FALSE if the file is already open in the console session
+ manager. This will be the case for stdin, stdout, and
+ stderr. If NewOepn is TRUE, we're opening /dev/tty an
+ additional time, and we want the console sess mgr to open
+ con: again, to go along.
+
+Return Value:
+
+ A posix error status, or 0 if successful.
+
+--*/
+{
+ SCREQUESTMSG Request;
+ NTSTATUS Status;
+
+ Fd->SystemOpenFileDesc = AllocateSystemOpenFile();
+ if (NULL == Fd->SystemOpenFileDesc) {
+ return ENOMEM;
+ }
+
+ if (DesiredAccess & FILE_READ_DATA) {
+ Fd->SystemOpenFileDesc->Flags |= PSX_FD_READ;
+ }
+ if (DesiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA)) {
+ Fd->SystemOpenFileDesc->Flags |= PSX_FD_WRITE;
+ }
+
+ if (Flags & O_NONBLOCK) {
+ Fd->SystemOpenFileDesc->Flags |= PSX_FD_NOBLOCK;
+ }
+ if (Flags & O_APPEND) {
+ Fd->SystemOpenFileDesc->Flags |= PSX_FD_APPEND;
+ }
+
+ if (ReferenceOrCreateIoNode(PSX_CONSOLE_DEV,
+ (ino_t)p->PsxSession->Terminal->ConsolePort, FALSE,
+ &Fd->SystemOpenFileDesc->IoNode)) {
+ // null
+ } else {
+ Fd->SystemOpenFileDesc->IoNode->IoVectors = &ConVectors;
+ Fd->SystemOpenFileDesc->IoNode->Mode = S_IFCHR | 0700;
+ Fd->SystemOpenFileDesc->IoNode->OwnerId = p->EffectiveUid;
+ Fd->SystemOpenFileDesc->IoNode->GroupId = p->EffectiveGid;
+ }
+ RtlLeaveCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
+
+ (void)IoOpenNewHandle(p, Fd, NULL);
+
+ Fd->SystemOpenFileDesc->Terminal = p->PsxSession->Terminal;
+
+ //
+ // Add a reference to the terminal (there is an additional
+ // file descriptor open on it).
+ //
+
+ ++Fd->SystemOpenFileDesc->Terminal->ReferenceCount;
+
+ if (!NewOpen) {
+ return 0;
+ }
+
+ //
+ // Send a message to posix.exe, asking him to open the
+ // console again.
+ //
+
+ Request.Request = ConRequest;
+ Request.d.Con.Request = ScOpenFile;
+
+ if (DesiredAccess & FILE_WRITE_DATA) {
+ Request.d.Con.d.IoBuf.Flags = O_WRONLY;
+ } else {
+ Request.d.Con.d.IoBuf.Flags = O_RDONLY;
+ }
+
+ PORT_MSG_TOTAL_LENGTH(Request) = sizeof(SCREQUESTMSG);
+ PORT_MSG_DATA_LENGTH(Request) = sizeof(SCREQUESTMSG) -
+ sizeof(PORT_MESSAGE);
+ PORT_MSG_ZERO_INIT(Request) = 0;
+
+ // XXX.mjb: hold session mgr lock
+
+ Status = NtRequestWaitReplyPort(p->PsxSession->Terminal->ConsolePort,
+ (PPORT_MESSAGE)&Request, (PPORT_MESSAGE)&Request);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: NtRequest: 0x%x\n", Status));
+ }
+ ASSERT(NT_SUCCESS(Status));
+
+ // XXX.mjb: release session mgr lock
+
+ Fd->SystemOpenFileDesc->NtIoHandle = Request.d.Con.d.IoBuf.Handle;
+
+ return 0;
+}
+
+
+//
+// Internal support routine
+//
+
+ULONG
+OpenDevNull(
+ PPSX_PROCESS p,
+ PFILEDESCRIPTOR Fd,
+ ULONG DesiredAccess,
+ ULONG Flags
+ )
+/*++
+
+Routine Description:
+
+ This routine is called to implement an open on the file /dev/null.
+
+Arguments:
+
+ p - The process on whose behalf the open is performed.
+ Fd - The file descriptor to receive the handle.
+ DesiredAccess - The access requested on the file.
+ Flags -
+ NewOpen - FALSE if the file is already open in the console session
+ manager. This will be the case for stdin, stdout, and
+ stderr. If NewOepn is TRUE, we're opening /dev/tty an
+ additional time, and we want the console sess mgr to open
+ con: again, to go along.
+
+
+Return Value:
+
+ A posix error status, or 0 if successful.
+
+--*/
+{
+ NTSTATUS Status;
+
+ Fd->SystemOpenFileDesc = AllocateSystemOpenFile();
+ if (NULL == Fd->SystemOpenFileDesc) {
+ return ENOMEM;
+ }
+
+ if (DesiredAccess & FILE_READ_DATA) {
+ Fd->SystemOpenFileDesc->Flags |= PSX_FD_READ;
+ }
+ if (DesiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA)) {
+ Fd->SystemOpenFileDesc->Flags |= PSX_FD_WRITE;
+ }
+
+ if (Flags & O_NONBLOCK) {
+ Fd->SystemOpenFileDesc->Flags |= PSX_FD_NOBLOCK;
+ }
+ if (Flags & O_APPEND) {
+ Fd->SystemOpenFileDesc->Flags |= PSX_FD_APPEND;
+ }
+
+ if (ReferenceOrCreateIoNode(PSX_NULL_DEV,
+ (ino_t)0, FALSE, &Fd->SystemOpenFileDesc->IoNode)) {
+ // null
+ } else {
+ Fd->SystemOpenFileDesc->IoNode->IoVectors = &NullVectors;
+ Fd->SystemOpenFileDesc->IoNode->Mode = S_IFCHR | 0666;
+ Fd->SystemOpenFileDesc->IoNode->OwnerId = p->EffectiveUid;
+ Fd->SystemOpenFileDesc->IoNode->GroupId = p->EffectiveGid;
+ }
+ RtlLeaveCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
+
+ (void)IoOpenNewHandle(p, Fd, NULL);
+
+ Fd->SystemOpenFileDesc->Terminal = NULL;
+
+ return 0;
+}
+
+//
+// Internal support routine
+//
+
+dev_t
+GetFileDeviceNumber(
+ PUNICODE_STRING Path
+ )
+/*++
+
+Routine Description:
+
+ Takes a path, like \DosDevices\X:\foo\bar, and returns
+ the device number, which in this case is 'X'.
+
+Arguments:
+
+ Path - the path.
+
+Return Value:
+
+ The device number.
+--*/
+{
+ wchar_t ch;
+
+ ch = Path->Buffer[12];
+ return (dev_t)ch;
+}
+
diff --git a/private/posix/psxss/srvinit.c b/private/posix/psxss/srvinit.c
new file mode 100644
index 000000000..4506699fd
--- /dev/null
+++ b/private/posix/psxss/srvinit.c
@@ -0,0 +1,262 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ srvinit.c
+
+Abstract:
+
+ This is the main initialization module for the POSIX Subsystem Server
+
+Author:
+
+ Mark Lucovsky (markl) 08-Mar-1989
+
+Revision History:
+
+ Ellen Aycock-Wright (ellena) 15-Jul-1989 Updated
+
+--*/
+
+#include <time.h>
+#include <wchar.h>
+#include "psxsrv.h"
+
+void RemoveJunkDirectories(void);
+
+NTSTATUS
+PsxServerInitialization(VOID)
+{
+ NTSTATUS Status;
+ BOOLEAN b;
+
+ //
+ // Use the process heap for memory allocation.
+ //
+
+ PsxHeap = RtlProcessHeap();
+
+ //
+ // Initialize the Session List
+ //
+
+ Status = PsxInitializeNtSessionList();
+ ASSERT( NT_SUCCESS( Status ) );
+
+ //
+ // Initialize the Process List
+ //
+
+ Status = PsxInitializeProcessStructure();
+ ASSERT( NT_SUCCESS( Status ) );
+
+ //
+ // Initialize the I/O related functions
+ //
+
+ Status = PsxInitializeIO();
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Initialize the POSIX Server Console Session Port, the listen thread
+ // and one request thread.
+ //
+
+ Status = InitConnectingTerminalList();
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ Status = PsxInitializeConsolePort();
+ ASSERT( NT_SUCCESS( Status ) );
+
+
+ //
+ // Initialize the POSIX Server API Port, the listen thread and one or more
+ // request threads.
+ //
+
+ PsxNumberApiRequestThreads = 1;
+ Status = PsxApiPortInitialize();
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Initialize the Posix Server Sid cache.
+ //
+
+ InitSidList();
+
+ //
+ // Enable the backup privilege.
+ //
+
+ Status = RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, TRUE, FALSE, &b);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: AdjustPrivilege: 0x%x\n", Status));
+ }
+
+ RemoveJunkDirectories();
+
+ return Status;
+}
+
+NTSTATUS
+PsxInitializeIO(VOID)
+
+/*++
+
+Routine Description:
+
+ This function initializes all of the Io related functions in PSX.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status.
+
+--*/
+
+{
+
+ LONG i;
+ NTSTATUS st;
+
+ //
+ // Initialize SystemOpenFileLock and IoNodeHashTableLock
+ //
+
+ st = RtlInitializeCriticalSection(&SystemOpenFileLock);
+ ASSERT(NT_SUCCESS(st));
+
+ st = RtlInitializeCriticalSection(&IoNodeHashTableLock);
+ ASSERT(NT_SUCCESS(st));
+
+ //
+ // Initialize the IoNodeHashTable
+ //
+
+ for (i = 0; i < IONODEHASHSIZE; i++ ) {
+ InitializeListHead(&IoNodeHashTable[i]);
+ }
+
+ return (st);
+
+}
+
+void
+RemoveJunkDirectories(void)
+{
+ OBJECT_ATTRIBUTES Obj;
+ NTSTATUS Status;
+ HANDLE hDir;
+ UNICODE_STRING U, U2, HardDisk_U;
+ wchar_t wc;
+ UCHAR Buf[sizeof(FILE_NAMES_INFORMATION) +
+ NAME_MAX * sizeof(WCHAR)];
+ PFILE_NAMES_INFORMATION pNamesInfo = (PVOID)Buf;
+ FILE_DISPOSITION_INFORMATION Disp;
+ IO_STATUS_BLOCK Iosb;
+ HANDLE hFile;
+ wchar_t buf[512];
+ wchar_t buf2[512];
+
+ RtlInitUnicodeString(&HardDisk_U, L"\\Device\\Harddisk");
+
+ //
+ // If there are "psxjunk" directories in the root of any filesystems,
+ // we remove them and any files that may happen to be in them...
+ //
+
+ for (wc = L'A'; wc <= L'Z'; wc++) {
+
+ swprintf(buf, L"\\DosDevices\\%wc:", wc);
+ U.Buffer = buf;
+ U.Length = wcslen(buf) * sizeof(wchar_t);
+ U.MaximumLength = sizeof(buf);
+
+ InitializeObjectAttributes(&Obj, &U, 0, NULL, NULL);
+
+ Status = NtOpenSymbolicLinkObject(&hDir, SYMBOLIC_LINK_QUERY,
+ &Obj);
+ if (!NT_SUCCESS(Status)) {
+ continue;
+ }
+
+ U2.Buffer = buf2;
+ U2.Length = 0;
+ U2.MaximumLength = sizeof(buf2);
+
+ Status = NtQuerySymbolicLinkObject(hDir, &U2, NULL);
+ NtClose(hDir);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: NtQuerySymLink: 0x%x\n", Status));
+ continue;
+ }
+
+ if (!RtlPrefixUnicodeString(&HardDisk_U, &U2, TRUE)) {
+ // Symlink does not point to hard disk.
+ continue;
+ }
+
+ Status = RtlAppendUnicodeToString(&U, L"\\");
+ ASSERT(NT_SUCCESS(Status));
+ Status = RtlAppendUnicodeToString(&U, PSX_JUNK_DIR);
+ ASSERT(NT_SUCCESS(Status));
+
+ InitializeObjectAttributes(&Obj, &U, 0, NULL, NULL);
+
+ Status = NtOpenFile(&hDir, SYNCHRONIZE | DELETE | GENERIC_READ,
+ &Obj, &Iosb, SHARE_ALL,
+ FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
+ if (!NT_SUCCESS(Status)) {
+ continue;
+ }
+
+ //
+ // Delete all files in the directory.
+ //
+
+ for (;;) {
+ Status = NtQueryDirectoryFile(hDir, NULL, NULL, NULL, &Iosb,
+ &Buf, sizeof(Buf), FileNamesInformation, TRUE, NULL,
+ FALSE);
+ if (STATUS_NO_MORE_FILES == Status) {
+ break;
+ }
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("NtQueryDirectoryFile: 0x%x\n", Status));
+ break;
+ }
+
+ U.Length = U.MaximumLength = (USHORT)pNamesInfo->FileNameLength;
+ U.Buffer = pNamesInfo->FileName;
+
+ InitializeObjectAttributes(&Obj, &U, 0, hDir, NULL);
+
+ Status = NtOpenFile(&hFile, SYNCHRONIZE | DELETE,
+ &Obj, &Iosb, SHARE_ALL,
+ FILE_SYNCHRONOUS_IO_NONALERT);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("NtOpenFile: %wZ\n", &U));
+ KdPrint(("NtOpenFile: 0x%x\n", Status));
+ continue;
+ }
+ Disp.DeleteFile = TRUE;
+ Status = NtSetInformationFile(hFile, &Iosb, &Disp,
+ sizeof(Disp), FileDispositionInformation);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("NtSetInfoFile: 0x%x\n", Status));
+ }
+ NtClose(hFile);
+ }
+ Disp.DeleteFile = TRUE;
+ Status = NtSetInformationFile(hDir, &Iosb, &Disp,
+ sizeof(Disp), FileDispositionInformation);
+ NtClose(hDir);
+ }
+}
diff --git a/private/posix/psxss/srvtask.c b/private/posix/psxss/srvtask.c
new file mode 100644
index 000000000..1dd96558e
--- /dev/null
+++ b/private/posix/psxss/srvtask.c
@@ -0,0 +1,1948 @@
+/*--
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ srvtask.c
+
+Abstract:
+
+ Implementation of PSX Process Structure APIs.
+
+Author:
+
+ Mark Lucovsky (markl) 08-Mar-1989
+
+Revision History:
+
+--*/
+
+#include "psxsrv.h"
+
+#ifdef _MIPS_
+#include "psxmips.h"
+#endif
+
+#ifdef _ALPHA_
+#include "psxalpha.h"
+#endif
+
+#ifdef _X86_
+#include "psxi386.h"
+#endif
+
+#ifdef _PPC_
+#include "psxppc.h"
+#endif
+
+#include <time.h>
+
+#include <windef.h>
+#include <winbase.h>
+#undef DeleteFile
+
+extern VOID
+PsxFreeDirectories(
+ IN PPSX_PROCESS
+ );
+
+VOID
+ConvertPathToWin(char *path);
+
+BOOLEAN
+PsxFork(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+
+/*++
+
+Routine Description:
+
+ This function implements posix fork() API
+
+Arguments:
+
+ p - Supplies the address of the calling process
+
+ m - Supplies the address of the related message
+
+Return Value:
+
+ TRUE - Always succeeds and generates a reply
+
+--*/
+
+{
+ PPSX_PROCESS ForkProcess, NewProcess;
+ HANDLE NewProcessHandle, NewThreadHandle;
+ THREAD_BASIC_INFORMATION ThreadBasicInfo;
+ PVOID ExcptList;
+ CONTEXT Context;
+ ULONG Psp;
+ NTSTATUS st;
+ INITIAL_TEB InitialTeb;
+ CLIENT_ID ClientId;
+ PPSX_FORK_MSG args;
+ HANDLE h;
+
+ args = &m->u.Fork;
+
+ if (p->Flags & P_NO_FORK) {
+
+ //
+ // This process may not fork; it's context has been
+ // rearranged to make it call the PdxNullApiCaller.
+ //
+
+ m->Error = EINTR;
+ m->Signal = SIGCONT;
+ return TRUE;
+ }
+
+ //
+ // Impersonate the client to insure that the new process will belong
+ // to him instead of to us.
+ //
+
+ st = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
+ ASSERT(NT_SUCCESS(st));
+
+ //
+ // Create a new process to be the child. The ExceptionPort is
+ // initialized to PsxApiPort.
+ //
+
+ st = NtCreateProcess(&NewProcessHandle, PROCESS_ALL_ACCESS, NULL,
+ p->Process, TRUE, NULL, NULL, PsxApiPort);
+ if (!NT_SUCCESS(st)) {
+ EndImpersonation();
+ m->Error = EAGAIN;
+ return TRUE;
+ }
+
+ {
+ ULONG HardErrorMode = 0; // disable popups
+ st = NtSetInformationProcess(
+ NewProcessHandle,
+ ProcessDefaultHardErrorMode,
+ (PVOID)&HardErrorMode,
+ sizeof(HardErrorMode)
+ );
+ ASSERT(NT_SUCCESS(st));
+ }
+
+ Context.ContextFlags = CONTEXT_FULL;
+
+ st = NtGetContextThread(p->Thread, &Context);
+ ASSERT(NT_SUCCESS(st));
+
+ InitialTeb.OldInitialTeb.OldStackBase = NULL;
+ InitialTeb.OldInitialTeb.OldStackLimit = NULL;
+ InitialTeb.StackBase = args->StackBase;
+ InitialTeb.StackLimit = args->StackLimit;
+ InitialTeb.StackAllocationBase = args->StackAllocationBase;
+
+ SetPsxForkReturn(Context);
+
+ st = NtCreateThread(&NewThreadHandle, THREAD_ALL_ACCESS, NULL,
+ NewProcessHandle, &ClientId, &Context, &InitialTeb, TRUE);
+
+ EndImpersonation();
+
+ if (!NT_SUCCESS(st)) {
+ st = NtTerminateProcess(NewProcessHandle, STATUS_SUCCESS);
+ ASSERT(NT_SUCCESS(st));
+ NtWaitForSingleObject(NewProcessHandle, FALSE, NULL);
+ NtClose(NewProcessHandle);
+ m->Error = EAGAIN;
+ return TRUE;
+ }
+
+ //
+ // Allocate a process structure.
+ //
+
+ NewProcess = PsxAllocateProcess(&ClientId);
+
+ if (NULL == NewProcess) {
+ st = NtTerminateProcess(NewProcessHandle, STATUS_SUCCESS);
+ ASSERT(NT_SUCCESS(st));
+ st = NtResumeThread(NewThreadHandle,&Psp);
+ ASSERT(NT_SUCCESS(st));
+ NtWaitForSingleObject(NewProcessHandle, FALSE, NULL);
+ NtClose(NewProcessHandle);
+ NtClose(NewThreadHandle);
+ m->Error = EAGAIN;
+ return TRUE;
+ }
+
+ //
+ // Copy the ExceptionList pointer from the parent process TEB
+ // to the child process TEB.
+ //
+
+ st = NtQueryInformationThread(p->Thread, ThreadBasicInformation,
+ (PVOID)&ThreadBasicInfo, sizeof(ThreadBasicInfo), NULL);
+ ASSERT(NT_SUCCESS(st));
+
+ // XXX.mjb: The actual address we want to read is
+ // TebBaseAddress->NtTib.ExceptionList, but I happen to know
+ // that these are equivalent.
+ //
+
+ st = NtReadVirtualMemory(p->Process,
+ (PVOID)ThreadBasicInfo.TebBaseAddress,
+ (PVOID)&ExcptList, sizeof(ExcptList), NULL);
+ ASSERT(NT_SUCCESS(st));
+
+ st = NtQueryInformationThread(NewThreadHandle, ThreadBasicInformation,
+ (PVOID)&ThreadBasicInfo, sizeof(ThreadBasicInfo), NULL);
+ ASSERT(NT_SUCCESS(st));
+
+ st = NtWriteVirtualMemory(NewProcessHandle,
+ (PVOID)ThreadBasicInfo.TebBaseAddress,
+ (PVOID)&ExcptList, sizeof(ExcptList), NULL);
+ ASSERT(NT_SUCCESS(st));
+
+
+ ForkProcess = p;
+
+ //
+ // The new process is allocated locked, but we need to lock the
+ // parent (ForkProcess).
+ //
+ AcquireProcessLock(ForkProcess);
+
+ st = PsxInitializeProcess(NewProcess, ForkProcess, 0L, NewProcessHandle,
+ NewThreadHandle, NULL);
+ if (!NT_SUCCESS(st)) {
+ st = NtTerminateProcess(NewProcessHandle, STATUS_SUCCESS);
+ ASSERT(NT_SUCCESS(st));
+ st = NtResumeThread(NewThreadHandle,&Psp);
+ ASSERT(NT_SUCCESS(st));
+ st = NtWaitForSingleObject(NewProcessHandle, FALSE, NULL);
+ NtClose(NewProcessHandle);
+ NtClose(NewThreadHandle);
+ m->Error = EAGAIN;
+ return TRUE;
+ }
+
+ NewProcess->InitialPebPsxData.Length =
+ sizeof(ForkProcess->InitialPebPsxData);
+
+ NewProcess->InitialPebPsxData.ClientStartAddress = NULL;
+ if (NULL != ForkProcess->PsxSession->Terminal) {
+ NewProcess->InitialPebPsxData.SessionPortHandle =
+ (HANDLE)ForkProcess->PsxSession->Terminal->UniqueId;
+ } else {
+ //
+ // What if there are no open file descriptors?
+ //
+
+ if (NULL != ForkProcess->ProcessFileTable[0].SystemOpenFileDesc)
+ NewProcess->InitialPebPsxData.SessionPortHandle =
+ (HANDLE)ForkProcess->ProcessFileTable[0].SystemOpenFileDesc->Terminal->UniqueId;
+ }
+
+ m->ReturnValue = NewProcess->Pid;
+
+
+ st = NtResumeThread(NewThreadHandle, &Psp);
+
+ ASSERT(NT_SUCCESS(st));
+
+Sleep(250);
+ return TRUE;
+}
+
+BOOLEAN
+PsxExec(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+
+/*++
+
+Routine Description:
+
+ This function implements posix execve() API
+
+Arguments:
+
+ p - Supplies the address of the calling process
+
+ m - Supplies the address of the related message
+
+Return Value:
+
+ TRUE - Always succeeds and generates a reply
+
+--*/
+
+{
+ RTL_USER_PROCESS_INFORMATION ProcInfo;
+ PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
+ PPSX_EXEC_MSG args;
+ WCHAR ImageFileString[1024];
+ ANSI_STRING CommandLine;
+ ANSI_STRING CWD;
+ NTSTATUS Status;
+ ULONG whocares;
+ ULONG i;
+ KERNEL_USER_TIMES ProcessTime;
+ ULONG PosixTime, Remainder;
+ UNICODE_STRING uCWD, uImageFileName;
+ PUNICODE_STRING u;
+ PVOID SaveDirectoryPrefix;
+ HANDLE h;
+
+ args = &m->u.Exec;
+
+ //
+ // If pathname is too big, then return error. This should
+ // really allow for names that are longer because of PSX
+ // prefixes
+ //
+
+ if (args->Path.Length > PATH_MAX) {
+ m->Error = ENAMETOOLONG;
+ return TRUE;
+ }
+
+ AcquireProcessLock(p);
+ if (Exited == p->State) {
+ ReleaseProcessLock(p);
+ return FALSE;
+ }
+
+ //
+ // Capture the pathname
+ //
+
+ Status = NtReadVirtualMemory(p->Process, args->Path.Buffer,
+ &ImageFileString[0], args->Path.Length, &whocares);
+
+ if (!NT_SUCCESS(Status)) {
+ ReleaseProcessLock(p);
+ m->Error = ENOMEM;
+ return TRUE;
+ }
+
+ uImageFileName.Buffer = &ImageFileString[0];
+ uImageFileName.Length = args->Path.Length;
+ uImageFileName.MaximumLength = args->Path.Length;
+
+ //
+ // Propagate Current Working Directory
+ //
+
+ SaveDirectoryPrefix = (PVOID)p->DirectoryPrefix;
+
+ if (!PsxPropagateDirectories(p)) {
+ ReleaseProcessLock(p);
+ p->DirectoryPrefix = SaveDirectoryPrefix;
+ m->Error = ENOMEM;
+ return TRUE;
+ }
+
+ //
+ // Format the process parameters
+ //
+
+ CommandLine.Buffer = args->Args;
+ CommandLine.Length = ARG_MAX;
+ CommandLine.MaximumLength = ARG_MAX;
+
+ CWD.Buffer = p->DirectoryPrefix->NtCurrentWorkingDirectory.Buffer +
+ strlen("\\DosDevices\\");
+ CWD.MaximumLength = p->DirectoryPrefix->NtCurrentWorkingDirectory.Length -
+ strlen("\\DosDevices\\");
+ CWD.Length = CWD.MaximumLength;
+ Status = RtlAnsiStringToUnicodeString(&uCWD, &CWD, TRUE);
+ if (!NT_SUCCESS(Status)) {
+ PsxFreeDirectories(p);
+ p->DirectoryPrefix = SaveDirectoryPrefix;
+ ReleaseProcessLock(p);
+ m->Error = ENOMEM;
+ return TRUE;
+ }
+
+ //
+ // Somewhere along the line someone changes the old process's
+ // DllPath to Unicode. If we pass a NULL DllPath here, we find
+ // that Ansi is expected, and we can't find the Dll. So here we
+ // take the Unicode DllPath, convert to Ansi, and pass it
+ // explicitly.
+ //
+
+ u = (PUNICODE_STRING)&NtCurrentPeb()->ProcessParameters->DllPath;
+ Status = RtlCreateProcessParameters(&ProcessParameters, &uImageFileName,
+ u, &uCWD, (PUNICODE_STRING)&CommandLine, NULL, NULL, NULL, NULL, NULL);
+
+#ifndef EXEC_FOREIGN
+ RtlFreeUnicodeString(&uCWD);
+#endif
+
+ if (!NT_SUCCESS(Status)) {
+#ifdef EXEC_FOREIGN
+ RtlFreeUnicodeString(&uCWD);
+#endif
+ PsxFreeDirectories(p);
+ p->DirectoryPrefix = SaveDirectoryPrefix;
+ ReleaseProcessLock(p);
+ m->Error = ENOMEM;
+ return TRUE;
+ }
+
+ //
+ // Create the process and thread. We impersonate the client so that
+ // they end up being owned by him, instead of owned by us.
+ //
+
+ Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = RtlCreateUserProcess(&uImageFileName, 0, ProcessParameters,
+ NULL, NULL, p->Process, FALSE, NULL, NULL, &ProcInfo);
+
+ EndImpersonation();
+
+ if (!NT_SUCCESS(Status)) {
+#ifdef EXEC_FOREIGN
+ RtlFreeUnicodeString(&uCWD);
+#endif
+
+ PsxFreeDirectories(p);
+ p->DirectoryPrefix = SaveDirectoryPrefix;
+ ReleaseProcessLock(p);
+
+ if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
+ m->Error = PsxStatusToErrnoPath(&uImageFileName);
+ return TRUE;
+ }
+ m->Error = PsxStatusToErrno(Status);
+ return TRUE;
+ }
+
+ RtlDestroyProcessParameters(ProcessParameters);
+
+ //
+ // Set the exception port for the new process so we'll find out
+ // if he takes a fault.
+ //
+
+ Status = NtSetInformationProcess(ProcInfo.Process,
+ ProcessExceptionPort, (PVOID)&PsxApiPort, sizeof(PsxApiPort));
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: NtSetInfoProcess: 0x%x\n", Status));
+ }
+ ASSERT(NT_SUCCESS(Status));
+
+ {
+ ULONG HardErrorMode = 0; // disable popups
+ Status = NtSetInformationProcess(
+ ProcInfo.Process,
+ ProcessDefaultHardErrorMode,
+ (PVOID)&HardErrorMode,
+ sizeof(HardErrorMode)
+ );
+ ASSERT(NT_SUCCESS(Status));
+ }
+
+ //
+ // check to make sure it is a POSIX app
+ //
+
+ if (ProcInfo.ImageInformation.SubSystemType !=
+ IMAGE_SUBSYSTEM_POSIX_CUI) {
+
+ //
+ // The image is not a Posix program. Tear down the
+ // process we just created in the usual way, and then
+ // call the windows subsystem to do the same thing
+ // over.
+ //
+
+ PsxFreeDirectories(p);
+ p->DirectoryPrefix = SaveDirectoryPrefix;
+#ifndef EXEC_FOREIGN
+ ReleaseProcessLock(p);
+#endif
+ Status = NtTerminateProcess(ProcInfo.Process, STATUS_SUCCESS);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: NtTerminateProcess: 0x%x\n", Status));
+ }
+ Status = NtWaitForSingleObject(ProcInfo.Process, FALSE, NULL);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: NtWaitForSingleObject: 0x%x\n", Status));
+ }
+#ifdef EXEC_FOREIGN
+ Status = ExecForeignImage(p, m, &uImageFileName, &uCWD);
+ ReleaseProcessLock(p);
+ RtlFreeUnicodeString(&uCWD);
+ if (!NT_SUCCESS(Status)) {
+ m->Error = ENOEXEC;
+ return TRUE;
+ }
+ return FALSE;
+#else
+ m->Error = ENOEXEC;
+ return TRUE;
+#endif
+
+ }
+#ifdef EXEC_FOREIGN
+ RtlFreeUnicodeString(&uCWD);
+#endif
+
+ //
+ // Close open files that have their close-on-exec bit set.
+ //
+
+ for (i = 0; i < OPEN_MAX; ++i) {
+ if (NULL != p->ProcessFileTable[i].SystemOpenFileDesc &&
+ p->ProcessFileTable[i].Flags & PSX_FD_CLOSE_ON_EXEC) {
+ (void)DeallocateFd(p, i);
+ }
+ }
+
+ AcquireProcessStructureLock();
+
+ //
+ // Get the time for this process and add to the accumulated time for
+ // the process
+ //
+
+ Status = NtQueryInformationProcess(p->Process, ProcessTimes,
+ (PVOID)&ProcessTime, sizeof(KERNEL_USER_TIMES), NULL);
+ ASSERT(NT_SUCCESS(Status));
+
+ PosixTime = RtlExtendedLargeIntegerDivide(ProcessTime.KernelTime,
+ 10000, &Remainder).LowPart;
+ p->ProcessTimes.tms_stime += PosixTime;
+
+ PosixTime = RtlExtendedLargeIntegerDivide(ProcessTime.UserTime,
+ 10000, &Remainder).LowPart;
+ p->ProcessTimes.tms_utime += PosixTime;
+
+ //
+ // Terminate the current process, and munge in the
+ // new process
+ //
+
+ RemoveEntryList(&p->ClientIdHashLinks);
+ p->ClientIdHashLinks.Flink = p->ClientIdHashLinks.Blink = NULL;
+
+ Status = NtTerminateProcess(p->Process, STATUS_SUCCESS);
+ ASSERT(NT_SUCCESS(Status));
+ Status = NtWaitForSingleObject(p->Process, FALSE, NULL);
+ ASSERT(NT_SUCCESS(Status));
+
+
+ Status = NtClose(p->ClientPort);
+ ASSERT(NT_SUCCESS(Status));
+ Status = NtClose(p->Process);
+ ASSERT(NT_SUCCESS(Status));
+ Status = NtClose(p->Thread);
+ ASSERT(NT_SUCCESS(Status));
+
+ p->Process = ProcInfo.Process;
+ p->Thread = ProcInfo.Thread;
+ p->ClientId = ProcInfo.ClientId;
+ p->ClientPort = NULL;
+
+ InsertTailList(&ClientIdHashTable[CIDTOHASHINDEX(&p->ClientId)],
+ &p->ClientIdHashLinks);
+
+ p->Flags |= P_HAS_EXECED;
+
+ ReleaseProcessStructureLock();
+
+ //
+ // Restore signals being caught to SIG_DFL
+ // --- Posix does not specify what to do w/ associated flags or mask
+ //
+
+ for (i = 0; i < _SIGMAXSIGNO; i++) {
+ if (p->SignalDataBase.SignalDisposition[i].sa_handler !=
+ SIG_IGN) {
+ p->SignalDataBase.SignalDisposition[i].sa_handler =
+ SIG_DFL;
+ }
+ }
+
+ //
+ // Since we don't reply to the old process (he's gone now), we need
+ // to start the new process's InPsx count at zero.
+ //
+
+ p->InPsx = 0;
+
+ ReleaseProcessLock(p);
+ ExecProcessFileTable(p);
+
+ if (p->ProcessIsBeingDebugged && PsxpDebuggerActive) {
+ Status = NtSetInformationProcess(p->Process, ProcessDebugPort,
+ (PVOID)&PsxpDebugPort, sizeof(HANDLE));
+ if (!NT_SUCCESS(Status)) {
+ p->ProcessIsBeingDebugged = FALSE;
+ }
+ }
+
+ Status = NtResumeThread(p->Thread,&whocares);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: NtResumeThread: 0x%x\n", Status));
+ }
+ ASSERT(NT_SUCCESS(Status) && whocares == 1);
+
+ return FALSE;
+}
+
+BOOLEAN
+PsxGetIds(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+/*++
+
+Routine Description:
+
+ This function provides all the support needed to implement
+ getpid(), getppid(), getuid(), geteuid(), getgid(), and getegid().
+
+Arguments:
+
+ p - Supplies the address of the calling process
+
+ m - Supplies the address of the related message
+
+Return Value:
+
+ TRUE - Always succeeds and generates a reply
+
+--*/
+{
+ PPSX_GETIDS_MSG args;
+
+ args = &m->u.GetIds;
+
+ args->Pid = p->Pid;
+ args->ParentPid = p->ParentPid;
+ args->GroupId = p->ProcessGroupId;
+ args->RealUid = p->RealUid;
+ args->EffectiveUid = p->EffectiveUid;
+ args->RealGid = p->RealGid;
+ args->EffectiveGid = p->EffectiveGid;
+
+ return TRUE;
+}
+
+BOOLEAN
+PsxExit(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+/*++
+
+Routine Description:
+
+ This function implements the _exit() API.
+
+Arguments:
+
+ p - Supplies the address of the calling process
+
+ m - Supplies the address of the related message
+
+Return Value:
+
+ TRUE - Always succeeds and generates a reply
+
+--*/
+{
+ PPSX_EXIT_MSG args;
+
+ args = &m->u.Exit;
+
+ Exit(p, (args->ExitStatus & 0xff) << 8);
+
+ return FALSE;
+}
+
+VOID
+WaitPidHandler(
+ IN PPSX_PROCESS p,
+ IN PINTCB IntControlBlock,
+ IN PSX_INTERRUPTREASON InterruptReason,
+ IN int Signal
+ )
+/*++
+
+Routine Description:
+
+ This function is called whenever a process that is in a waitpid wait
+ is sent a signal, or has a child stop/terminate that could possibly
+ satisfy a wait.
+
+ This function is responsible for unlocking the process.
+
+Arguments:
+
+ p - Supplies the address of the process being interrupted.
+
+ IntControlBlock - Supplies the address of the interrupt control block.
+
+ InterruptReason - Supplies the reason that this process is being
+ interrupted. Not used in this handler.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PPSX_API_MSG m;
+ PPSX_WAITPID_MSG args;
+ PPSX_PROCESS cp;
+ BOOLEAN WaitSatisfied;
+ pid_t TargetProcess;
+ pid_t TargetGroup;
+ enum _WaitType { AnyProcess, SpecificProcess, SpecificGroup };
+ enum _WaitType WaitType;
+
+ RtlLeaveCriticalSection(&BlockLock);
+
+ m = IntControlBlock->IntMessage;
+
+ args = &m->u.WaitPid;
+
+ AcquireProcessStructureLock();
+
+ p->State = Active;
+
+ if (InterruptReason == SignalInterrupt) {
+
+ ReleaseProcessStructureLock();
+
+ RtlFreeHeap(PsxHeap, 0, (PVOID)IntControlBlock);
+
+ m->Error = EINTR;
+ m->Signal = Signal;
+ ApiReply(p,m,NULL);
+ RtlFreeHeap(PsxHeap, 0, (PVOID)m);
+ return;
+ }
+
+ WaitSatisfied = FALSE;
+
+ TargetProcess = SPECIALPID;
+ TargetGroup = SPECIALPID;
+ WaitType = AnyProcess;
+
+ if (args->Pid <= 0 && args->Pid != (pid_t)-1) {
+
+ WaitType = SpecificGroup;
+
+ //
+ // Process group id is specified.
+ //
+
+ if (args->Pid == 0) {
+ TargetGroup = p->ProcessGroupId;
+ } else {
+ TargetGroup = -1 * args->Pid;
+ }
+ } else {
+
+ if (args->Pid != (pid_t)-1) {
+ TargetProcess = args->Pid;
+ WaitType = SpecificProcess;
+ }
+ }
+
+ //
+ // Scan process table
+ //
+
+ for (cp = FirstProcess; cp < LastProcess; cp++) {
+
+ if (cp->Flags & P_FREE) {
+ continue;
+ }
+
+ //
+ // Just look at processes that could possibly satisfy a wait.
+ // - Processes that have exited
+ // - Stopped processes that have not previously satisfied a
+ // wait (if WUNTRACED was set)
+ //
+
+ if (cp->State == Exited ||
+ (cp->State == Stopped && (args->Options & WUNTRACED) &&
+ !(cp->Flags & P_WAITED))) {
+
+ if (cp->ParentPid != p->Pid) {
+ continue;
+ }
+
+ switch (WaitType) {
+
+ case AnyProcess:
+
+ m->ReturnValue = cp->Pid;
+ args->StatLocValue = cp->ExitStatus;
+ break;
+
+ case SpecificProcess:
+
+ if ( cp->Pid == TargetProcess ) {
+ m->ReturnValue = cp->Pid;
+ args->StatLocValue = cp->ExitStatus;
+ }
+ break;
+
+ case SpecificGroup:
+
+ if ( cp->ProcessGroupId == TargetGroup){
+ m->ReturnValue = cp->Pid;
+ args->StatLocValue = cp->ExitStatus;
+ }
+ break;
+ }
+
+ if ( m->ReturnValue ) {
+
+ //
+ // wait was satisfied
+ //
+
+ if ( cp->State == Exited ) {
+
+ p->ProcessTimes.tms_cstime += (cp->ProcessTimes.tms_stime
+ + cp->ProcessTimes.tms_cstime);
+ p->ProcessTimes.tms_cutime += (cp->ProcessTimes.tms_utime
+ + cp->ProcessTimes.tms_cutime);
+
+ //
+ // Deallocate the process
+ //
+
+ cp->Flags |= P_FREE;
+ RtlDeleteCriticalSection(&cp->ProcessLock);
+
+ } else {
+
+ //
+ // set bit so this stopped process won't satisfy another
+ // wait until it stops again or exits.
+ //
+
+ cp->Flags |= P_WAITED;
+ args->StatLocValue = cp->ExitStatus | (1L << 30);
+ }
+
+ WaitSatisfied = TRUE;
+
+ break;
+ }
+ }
+ }
+
+ if ( WaitSatisfied ) {
+
+ ReleaseProcessStructureLock();
+
+ RtlFreeHeap(PsxHeap, 0, (PVOID)IntControlBlock);
+ ApiReply(p,m,NULL);
+ RtlFreeHeap(PsxHeap, 0, (PVOID)m);
+ return;
+ }
+
+ //
+ // Rewait
+ //
+
+ p->State = Waiting;
+
+ (void)BlockProcess(p,
+ NULL,
+ WaitPidHandler,
+ m,
+ NULL,
+ &PsxProcessStructureLock);
+
+}
+
+
+
+BOOLEAN
+PsxWaitPid(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+
+/*++
+
+Routine Description:
+
+ This function implements the wait() and waitpid() APIs.
+
+Arguments:
+
+ p - Supplies the address of the calling process
+
+ m - Supplies the address of the related message
+
+Return Value:
+
+ TRUE - Always succeeds and generates a reply
+
+--*/
+
+{
+ NTSTATUS Status;
+ PPSX_WAITPID_MSG args;
+ PPSX_PROCESS cp;
+ pid_t TargetProcess;
+ pid_t TargetGroup;
+ BOOLEAN EmitEchild;
+ enum _WaitType { AnyProcess, SpecificProcess, SpecificGroup };
+ enum _WaitType WaitType;
+
+ args = &m->u.WaitPid;
+
+ //
+ // Test for invalid options
+ //
+
+ if (args->Options & ~(WNOHANG|WUNTRACED)) {
+ m->Error = EINVAL;
+ return TRUE;
+ }
+
+ TargetProcess = SPECIALPID;
+ TargetGroup = SPECIALPID;
+ WaitType = AnyProcess;
+
+ if (args->Pid <= 0 && args->Pid != (pid_t)-1) {
+
+ WaitType = SpecificGroup;
+
+ if (args->Pid == 0) {
+ TargetGroup = p->ProcessGroupId;
+ } else {
+ TargetGroup = -1 * args->Pid;
+ }
+ } else {
+ if (args->Pid != (pid_t)-1) {
+ TargetProcess = args->Pid;
+ WaitType = SpecificProcess;
+ }
+ }
+
+
+ AcquireProcessStructureLock();
+
+ EmitEchild = TRUE;
+
+ //
+ // Scan process table
+ //
+
+ for (cp = FirstProcess; cp < LastProcess; cp++) {
+ if (cp->Flags & P_FREE) {
+ continue;
+ }
+
+ //
+ // Until we know whether or not there is a process that
+ // could possibly satisfy a wait, we have to keep looking
+ // at all processes.
+ //
+
+ if (EmitEchild) {
+ if (WaitType == SpecificGroup) {
+ if (cp->ParentPid == p->Pid &&
+ cp->ProcessGroupId == TargetGroup) {
+ EmitEchild = FALSE;
+ }
+ } else {
+ if (cp->ParentPid == p->Pid) {
+ if (WaitType == SpecificProcess) {
+ if (cp->Pid == TargetProcess) {
+ EmitEchild = FALSE;
+ }
+ } else {
+ EmitEchild = FALSE;
+ }
+ }
+ }
+ }
+
+
+ //
+ // Just look at processes that could possibly satisfy a wait.
+ // - Processes that have exited
+ // - Stopped processes that have not previously satisfied
+ // a wait (if WUNTRACED was set)
+ //
+
+ if (cp->State == Exited ||
+ (cp->State == Stopped && (args->Options & WUNTRACED) &&
+ !(cp->Flags & P_WAITED))) {
+
+ if (cp->ParentPid != p->Pid) {
+ continue;
+ }
+
+ switch (WaitType) {
+ case AnyProcess:
+ m->ReturnValue = cp->Pid;
+ args->StatLocValue = cp->ExitStatus;
+ break;
+
+ case SpecificProcess:
+
+ if (cp->Pid == TargetProcess) {
+ m->ReturnValue = cp->Pid;
+ args->StatLocValue = cp->ExitStatus;
+ }
+ break;
+
+ case SpecificGroup:
+
+ if (cp->ProcessGroupId == TargetGroup) {
+ m->ReturnValue = cp->Pid;
+ args->StatLocValue = cp->ExitStatus;
+ }
+ break;
+ }
+
+ if (m->ReturnValue) {
+
+ //
+ // wait was satisfied
+ //
+
+ if ( cp->State == Exited ) {
+
+ p->ProcessTimes.tms_cstime += (cp->ProcessTimes.tms_stime
+ + cp->ProcessTimes.tms_cstime);
+ p->ProcessTimes.tms_cutime += (cp->ProcessTimes.tms_utime
+ + cp->ProcessTimes.tms_cutime);
+
+ //
+ // Deallocate the process
+ //
+
+ cp->Flags |= P_FREE;
+ RtlDeleteCriticalSection(&cp->ProcessLock);
+
+ } else {
+
+ //
+ // Set a bit to keep this stopped process from satisfying
+ // another wait.
+ //
+
+ cp->Flags |= P_WAITED;
+ args->StatLocValue = cp->ExitStatus | (1L << 30);
+ }
+
+ ReleaseProcessStructureLock();
+
+ return TRUE;
+ }
+ }
+ }
+
+ if (EmitEchild) {
+ m->Error = ECHILD;
+ ReleaseProcessStructureLock();
+ return TRUE;
+ }
+
+ if (args->Options & WNOHANG) {
+ m->ReturnValue = 0;
+ args->StatLocValue = 0;
+ ReleaseProcessStructureLock();
+ return TRUE;
+ }
+
+ //
+ // Make the process sleep until the wait can be satisfied.
+ //
+
+ p->State = Waiting;
+ Status = BlockProcess(p, NULL, WaitPidHandler, m, NULL,
+ &PsxProcessStructureLock);
+ if (!NT_SUCCESS(Status)) {
+ m->Error = PsxStatusToErrno(Status);
+ return TRUE;
+ }
+
+ //
+ // The process has successfully been blocked. Don't reply to the api
+ // request message.
+ //
+ return FALSE;
+}
+
+BOOLEAN
+PsxSetSid(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+
+/*++
+
+Routine Description:
+
+ This function implements the setsid() API.
+
+Arguments:
+
+ p - Supplies the address of the calling process
+
+ m - Supplies the address of the related message
+
+Return Value:
+
+ TRUE - Always succeeds and generates a reply
+
+--*/
+
+{
+ PPSX_SESSION OldSession;
+
+ //
+ // 1003.1-90 (4.3.2.4): EPERM when the calling process
+ // is already a process group leader.
+ //
+
+ if (p->Pid == p->ProcessGroupId) {
+ m->Error = EPERM;
+ return TRUE;
+ }
+
+ //
+ // Create a new session with no controlling tty and make
+ // the calling process the session leader.
+ //
+
+ AcquireProcessStructureLock();
+ LockNtSessionList();
+
+ RemoveEntryList(&p->GroupLinks);
+ InitializeListHead(&p->GroupLinks);
+
+ //
+ // Make the process the leader of his process group.
+ //
+
+ p->ProcessGroupId = p->Pid;
+
+ OldSession = p->PsxSession;
+ p->PsxSession = PsxAllocateSession(NULL, p->Pid);
+
+ UnlockNtSessionList();
+ ReleaseProcessStructureLock();
+
+ DEREFERENCE_PSX_SESSION(OldSession, 0);
+
+ m->ReturnValue = (pid_t)p->ProcessGroupId;
+
+ return TRUE;
+}
+
+BOOLEAN
+PsxSetPGroupId(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+
+/*++
+
+Routine Description:
+
+ This function implements the setpgid() API.
+
+Arguments:
+
+ p - Supplies the address of the calling process
+
+ m - Supplies the address of the related message
+
+Return Value:
+
+ TRUE - Always succeeds and generates a reply
+
+--*/
+
+{
+ PPSX_SETPGROUPID_MSG args;
+ pid_t TargetPid, TargetGroup;
+ PPSX_PROCESS cp, Target;
+ BOOLEAN EmitEsrch;
+
+ args = &m->u.SetPGroupId;
+
+ if (args->Pid < 0 || args->Pgid < 0) {
+ m->Error = EINVAL;
+ return TRUE;
+ }
+
+ TargetPid = (args->Pid ? args->Pid : p->Pid);
+ TargetGroup = (args->Pgid ? args->Pgid : TargetPid);
+
+ AcquireProcessStructureLock();
+ LockNtSessionList();
+
+ if ( p->Pid == TargetPid ) {
+ Target = p;
+ } else {
+
+ //
+ // Scan process table
+ //
+
+ EmitEsrch = TRUE;
+
+ for (cp = FirstProcess ;cp < LastProcess ;cp++ ) {
+
+ if ( cp->Flags & P_FREE ) {
+ continue;
+ }
+
+ if ( cp->ParentPid == p->Pid && cp->Pid == TargetPid ) {
+
+ EmitEsrch = FALSE;
+ Target = cp;
+ break;
+ }
+ }
+
+ if ( EmitEsrch ) {
+ m->Error = ESRCH;
+ goto done;
+
+ }
+ }
+
+ //
+ // If target is a child who has executed an exec, then report error
+ //
+
+ if (Target->ParentPid == p->Pid && (Target->Flags & P_HAS_EXECED)) {
+ m->Error = EACCES;
+ goto done;
+ }
+
+ //
+ // If target is a session leader, then report error
+ //
+
+ if ( Target->PsxSession->SessionLeader == TargetPid ) {
+ m->Error = EPERM;
+ goto done;
+ }
+
+ //
+ // If target is not in the same session as the calling process, then
+ // report error
+ //
+
+ if ( Target->PsxSession != p->PsxSession ) {
+ m->Error = EPERM;
+ goto done;
+ }
+
+ if ( TargetPid != TargetGroup ) {
+
+ //
+ // Scan process table looking for a pgrp id the same
+ // as TargetGroup and is in the same session is the calling process
+ //
+
+ EmitEsrch = TRUE;
+
+ for (cp = FirstProcess ;cp < LastProcess ;cp++ ) {
+
+ if ( cp->Flags & P_FREE ) {
+ continue;
+ }
+
+ if ( cp->ProcessGroupId == TargetGroup &&
+ p->PsxSession == cp->PsxSession ) {
+
+ EmitEsrch = FALSE;
+ break;
+ }
+ }
+
+ if ( EmitEsrch ) {
+ m->Error = EPERM;
+ goto done;
+ }
+ } else {
+ cp = Target;
+ }
+
+ //
+ // Everything is ok, so set the Target's pgrp id to the specified id
+ //
+
+ RemoveEntryList(&Target->GroupLinks);
+
+ if (cp != Target) {
+ InsertHeadList(&cp->GroupLinks, &Target->GroupLinks);
+ } else {
+ InitializeListHead(&Target->GroupLinks);
+ }
+
+ Target->ProcessGroupId = TargetGroup;
+
+done:
+ UnlockNtSessionList();
+ ReleaseProcessStructureLock();
+
+ return TRUE;
+}
+
+BOOLEAN
+PsxGetProcessTimes(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+
+/*++
+
+Routine Description:
+
+ This function implements the times() API.
+
+Arguments:
+
+ p - Supplies the address of the calling process
+
+ m - Supplies the address of the related message
+
+Return Value:
+
+ TRUE - Always succeeds and generates a reply
+
+--*/
+
+{
+ PPSX_GETPROCESSTIMES_MSG args;
+ NTSTATUS Status;
+ KERNEL_USER_TIMES ProcessTime;
+ ULONG PosixTime, Remainder;
+
+ ULONG LengthNeeded;
+
+ args = &m->u.GetProcessTimes;
+
+ {
+ LARGE_INTEGER DelayInterval;
+
+ DelayInterval.HighPart = 0;
+ DelayInterval.LowPart = 100;
+
+ NtDelayExecution(TRUE, &DelayInterval);
+ }
+
+ //
+ // Get the time for this process and add to the accumulated time for
+ // the process
+ //
+
+ Status = NtQueryInformationProcess(p->Process, ProcessTimes,
+ (PVOID)&ProcessTime, sizeof(ProcessTime), &LengthNeeded);
+ ASSERT(NT_SUCCESS(Status));
+
+ PosixTime = RtlExtendedLargeIntegerDivide(ProcessTime.UserTime,
+ 10000, &Remainder).LowPart;
+
+ args->ProcessTimes.tms_utime = p->ProcessTimes.tms_utime + PosixTime;
+
+ PosixTime = RtlExtendedLargeIntegerDivide(ProcessTime.KernelTime,
+ 10000, &Remainder).LowPart;
+
+ args->ProcessTimes.tms_stime = p->ProcessTimes.tms_stime + PosixTime;
+
+ args->ProcessTimes.tms_cutime = p->ProcessTimes.tms_cutime;
+ args->ProcessTimes.tms_cstime = p->ProcessTimes.tms_cstime;
+
+ return TRUE;
+}
+
+BOOLEAN
+PsxGetGroups(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+
+/*++
+
+Routine Description:
+
+ This function implements the getgroups() API.
+
+Arguments:
+
+ p - Supplies the address of the calling process
+
+ m - Supplies the address of the related message
+
+Return Value:
+
+ TRUE - Always succeeds and generates a reply
+
+XXX.mjb:
+
+ NT essentially puts no limit on the number of supplementary groups
+ a user may belong to. This is bad for Posix, since we want a limit,
+ and we want it small enough that people don't screw themselves by
+ allocating an array[NGROUPS_MAX] of gid_t.
+
+--*/
+
+{
+ PPSX_GETGROUPS_MSG args;
+ NTSTATUS Status;
+ HANDLE TokenHandle;
+ TOKEN_GROUPS *pGroups;
+ ULONG outlen, i, j;
+ gid_t *GroupList;
+
+ args = &m->u.GetGroups;
+
+ //
+ // Check args->GroupList for group array address validity.
+ //
+
+ //
+ // Examine the new process's token to figure out what the
+ // uid's should be.
+ //
+
+ Status = NtOpenProcessToken(p->Process, GENERIC_READ,
+ &TokenHandle);
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Get the supplemental groups.
+ //
+
+ Status = NtQueryInformationToken(TokenHandle, TokenGroups, NULL,
+ 0, &outlen);
+ ASSERT(STATUS_BUFFER_TOO_SMALL == Status);
+
+ pGroups = RtlAllocateHeap(PsxHeap, 0, outlen);
+ if (NULL == pGroups) {
+ //
+ // We don't have enough memory to hold the list of the process's
+ // groups. What is there to do except return an error?
+ //
+ NtClose(TokenHandle);
+ m->Error = ENOMEM;
+ return TRUE;
+ }
+
+ Status = NtQueryInformationToken(TokenHandle, TokenGroups, (PVOID)pGroups,
+ outlen, &outlen);
+
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: NtQueryInformationToken failed: 0x%x\n",
+ Status));
+ RtlFreeHeap(PsxHeap, 0, (PVOID)pGroups);
+ NtClose(TokenHandle);
+ m->Error = EACCES;
+ return TRUE;
+ }
+
+ //
+ // If the user has passed in a listsize of 0, then we only return
+ // the number of supplementary groups, without writing anything in
+ // the group array.
+ //
+ if (0 == args->NGroups) {
+ m->ReturnValue = pGroups->GroupCount;
+ m->Error = 0;
+
+ NtClose(TokenHandle);
+ RtlFreeHeap(PsxHeap, 0, (PVOID)pGroups);
+
+ return TRUE;
+ }
+
+ if ((ULONG)args->NGroups < pGroups->GroupCount) {
+ if (args->NGroups < NGROUPS_MAX) {
+ m->Error = EINVAL;
+ return TRUE;
+ }
+ //
+ // XXX.mjb: We're having a problem here. The caller has
+ // allocated space for the maximum number of groups that the
+ // user can have, according to the _NGROUPS_MAX limit, but
+ // the user actually has more groups than that. This can
+ // happen because NT's limit is indeterminate.
+ //
+
+ // ignore the groups that we cannot return.
+
+ pGroups->GroupCount = args->NGroups;
+ }
+
+ //
+ // Make an array of gid_t's and copy it to the user address space.
+ //
+
+ GroupList = RtlAllocateHeap(PsxHeap, 0, pGroups->GroupCount *
+ sizeof(gid_t));
+ if (NULL == GroupList) {
+ RtlFreeHeap(PsxHeap, 0, (PVOID)pGroups);
+ NtClose(TokenHandle);
+ m->Error = ENOMEM;
+ return TRUE;
+ }
+
+ for (i = 0, j = 0; i < pGroups->GroupCount; ++i) {
+ PSID Sid = pGroups->Groups[i].Sid;
+ GroupList[j] = MakePosixId(Sid);
+ if (0 != GroupList[j]) {
+ ++j;
+ }
+ }
+
+ //
+ // Copy GroupList to client's address space, at the place
+ // specified.
+ //
+
+ Status = NtWriteVirtualMemory(p->Process, args->GroupList,
+ GroupList, j * sizeof(gid_t), NULL);
+
+ RtlFreeHeap(PsxHeap, 0, (PVOID)GroupList);
+
+ if (!NT_SUCCESS(Status)) {
+ m->Error = PsxStatusToErrno(Status);
+ RtlFreeHeap(PsxHeap, 0, (PVOID)pGroups);
+ NtClose(TokenHandle);
+ return TRUE;
+ }
+
+ m->ReturnValue = j;
+ RtlFreeHeap(PsxHeap, 0, (PVOID)pGroups);
+ NtClose(TokenHandle);
+ return TRUE;
+}
+
+BOOLEAN
+PsxGetLogin(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+
+/*++
+
+Routine Description:
+
+ This function implements the getlogin() API.
+
+Arguments:
+
+ p - Supplies the address of the calling process
+
+ m - Supplies the address of the related message
+
+Return Value:
+
+ TRUE - Always succeeds and generates a reply
+
+ XXX.mjb: this routine is never called; getlogin() is implemented as
+ "getpwuid(getuid())->pw_name", kind of. See client-side getlogin().
+
+--*/
+
+{
+ PPSX_GETLOGIN_MSG args;
+ NTSTATUS Status;
+
+ args = &m->u.GetLogin;
+ args->LoginName; // w.r.t. our address space
+
+ m->Error = ENOSYS;
+ return TRUE;
+}
+
+BOOLEAN
+PsxSysconf(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+/*++
+
+Routine Description:
+
+ This function implements the sysconf() api.
+
+Arguments:
+
+ p - Supplies the address of the calling process.
+
+ m - Supplies the address of the related message.
+
+Return Value:
+
+ TRUE - Always succeeds and generates a reply.
+
+--*/
+
+{
+ PPSX_SYSCONF_MSG args;
+ NTSTATUS Status;
+ long value;
+ PPSX_PROCESS Process;
+
+ args = &m->u.Sysconf;
+
+ switch (args->Name) {
+ case _SC_ARG_MAX:
+ value = ARG_MAX;
+ break;
+ case _SC_CHILD_MAX:
+ //
+ // This is the reason sysconf is implemented in the
+ // server. We don't bother to grab any locks, since the
+ // result is out of date by the time it gets back to the
+ // user anyway.
+ //
+
+ value = 1;
+ for (Process = FirstProcess; Process < LastProcess; ++Process) {
+ if (Process->Flags & P_FREE) {
+ ++value;
+ }
+ }
+ break;
+
+ case _SC_CLK_TCK:
+ value = CLK_TCK;
+ break;
+ case _SC_NGROUPS_MAX:
+ value = NGROUPS_MAX;
+ break;
+ case _SC_OPEN_MAX:
+ value = OPEN_MAX;
+ break;
+ case _SC_JOB_CONTROL:
+#ifdef _POSIX_JOB_CONTROL
+ value = 1;
+ break;
+#else
+ value = 0;
+ break;
+#endif
+ case _SC_SAVED_IDS:
+#ifdef _POSIX_SAVED_IDS
+ value = 1;
+#else
+ value = 0;
+#endif
+ break;
+ case _SC_VERSION:
+ value = _POSIX_VERSION;
+ break;
+ case _SC_STREAM_MAX:
+ value = STREAM_MAX;
+ break;
+ case _SC_TZNAME_MAX:
+ value = TZNAME_MAX;
+ break;
+ default:
+ value = -1;
+ m->Error = EINVAL;
+ }
+ m->ReturnValue = value;
+ return TRUE;
+}
+
+#ifdef EXEC_FOREIGN
+
+#define UNICODE
+#include <windows.h>
+
+DWORD ForeignProcessWait(PVOID);
+
+NTSTATUS
+ExecForeignImage(
+ PPSX_PROCESS p,
+ PPSX_API_MSG m,
+ PUNICODE_STRING Image,
+ PUNICODE_STRING CurDir
+ )
+{
+ PPSX_EXEC_MSG args;
+ NTSTATUS Status;
+ LPWSTR CommandLine;
+ LPVOID Environment;
+ STARTUPINFO StartInfo;
+ PROCESS_INFORMATION ProcInfo;
+ BOOL Success;
+ char **ppch;
+ PWCHAR pwc;
+ ULONG flags;
+ ULONG ThreadId;
+ HANDLE ForeignProc;
+
+ args = &m->u.Exec;
+
+ //
+ // Convert argv array to command line format.
+ //
+
+ CommandLine = RtlAllocateHeap(PsxHeap, 0, ARG_MAX);
+ if (NULL == CommandLine) {
+ return STATUS_NO_MEMORY;
+ }
+ CommandLine[0] = 0;
+
+ ppch = (PVOID)args->Args;
+
+ for (ppch = (PVOID)args->Args; NULL != *ppch; ++ppch) {
+ ANSI_STRING A;
+ UNICODE_STRING U;
+
+ // this breaks on args with spaces in them
+
+ A.Buffer = *ppch + (ULONG)args->Args;
+ A.Length = A.MaximumLength = strlen(A.Buffer);
+
+ U.Buffer = &CommandLine[wcslen(CommandLine)];
+ U.Length = 0;
+ U.MaximumLength = ARG_MAX;
+
+ Status = RtlAnsiStringToUnicodeString(&U, &A, FALSE);
+ ASSERT(NT_SUCCESS(Status));
+
+ wcscat((PVOID)CommandLine, L" ");
+ }
+
+ Status = RtlCreateEnvironment(FALSE, &Environment);
+ if (!NT_SUCCESS(Status)) {
+ RtlFreeHeap(PsxHeap, 0, CommandLine);
+ return Status;
+ }
+
+ for (++ppch; NULL != *ppch; ++ppch) {
+ ANSI_STRING aName, aValue;
+ UNICODE_STRING nU, vU;
+ char *pch;
+
+ pch = strchr(*ppch + (ULONG)args->Args, '=');
+ ASSERT(NULL != pch);
+ *pch = '\0';
+
+ aName.Buffer = *ppch + (ULONG)args->Args;
+ aName.Length = strlen(aName.Buffer);
+ aName.MaximumLength = aName.Length;
+
+ aValue.Buffer = pch + 1;
+ aValue.Length = strlen(aValue.Buffer);
+ aValue.MaximumLength = aName.Length;
+
+ if (0 == stricmp("PATH", aName.Buffer)) {
+ ConvertPathToWin(aValue.Buffer);
+ aValue.Length = strlen(aValue.Buffer);
+ }
+
+ *pch = '=';
+
+ Status = RtlAnsiStringToUnicodeString(&nU, &aName, TRUE);
+ if (!NT_SUCCESS(Status)) {
+ RtlFreeHeap(PsxHeap, 0, CommandLine);
+ RtlDestroyEnvironment(Environment);
+ return Status;
+ }
+
+ Status = RtlAnsiStringToUnicodeString(&vU, &aValue, TRUE);
+ if (!NT_SUCCESS(Status)) {
+ RtlFreeHeap(PsxHeap, 0, nU.Buffer);
+ RtlFreeHeap(PsxHeap, 0, CommandLine);
+ RtlDestroyEnvironment(Environment);
+ return Status;
+ }
+
+ Status = RtlSetEnvironmentVariable_U(&Environment, &nU, &vU);
+ RtlFreeHeap(PsxHeap, 0, nU.Buffer);
+ RtlFreeHeap(PsxHeap, 0, vU.Buffer);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: RtlSetEnvVar: 0x%x\n", Status));
+ RtlFreeHeap(PsxHeap, 0, CommandLine);
+ RtlDestroyEnvironment(Environment);
+ return Status;
+ }
+ }
+
+ // Make Image into correct format: "X:\path", return to original
+ // after we're done.
+
+ pwc = Image->Buffer;
+ Image->Buffer += (sizeof(L"\\DosDevices\\") - 2)/2;
+ Image->Length -= (sizeof(L"\\DosDevices\\") - 2);
+
+ flags = CREATE_SUSPENDED|CREATE_NEW_CONSOLE|
+ CREATE_NEW_PROCESS_GROUP|CREATE_UNICODE_ENVIRONMENT;
+
+ // set up StartInfo
+
+ StartInfo.cb = sizeof(STARTUPINFO);
+ StartInfo.lpReserved = 0;
+ StartInfo.lpDesktop = NULL;
+ StartInfo.lpTitle = NULL;
+ StartInfo.dwX = StartInfo.dwY = 0;
+ StartInfo.dwXSize = StartInfo.dwYSize = 400;
+ StartInfo.dwFlags = 0;
+ StartInfo.wShowWindow = SW_SHOWDEFAULT;
+ StartInfo.cbReserved2 = 0;
+ StartInfo.lpReserved2 = NULL;
+
+ Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
+ ASSERT(NT_SUCCESS(Status));
+
+ Success = CreateProcess(
+ Image->Buffer, // address of module name
+ CommandLine, // command line
+ NULL, // process security attr
+ NULL, // thread security attr
+ FALSE, // inherit handles
+ flags, // creation flags
+ Environment, // address of new environ
+ CurDir->Buffer, // current working dir
+ &StartInfo, // startup info
+ &ProcInfo // process information
+ );
+
+ EndImpersonation();
+
+ // restore image name
+ Image->Buffer = pwc;
+
+ Status = RtlDestroyEnvironment(Environment);
+ ASSERT(NT_SUCCESS(Status));
+
+ RtlFreeHeap(PsxHeap, 0, CommandLine);
+ if (!Success) {
+ KdPrint(("PSXSS: CreateProcess: %d\n", GetLastError()));
+ return STATUS_UNSUCCESSFUL;
+
+ }
+
+ p->Thread = ProcInfo.hThread;
+ p->Process = ProcInfo.hProcess;
+
+ // set bit in the process to indicate it's a foreign
+ // image type
+
+ p->Flags |= P_FOREIGN_EXEC;
+
+ // create additional thread in psxss to wait for new
+ // process to exit.
+
+ Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
+ ASSERT(NT_SUCCESS(Status));
+
+ p->BlockingThread = CreateThread(
+ NULL,
+ 0,
+ ForeignProcessWait,
+ (PVOID)p,
+ CREATE_SUSPENDED,
+ &ThreadId
+ );
+
+ EndImpersonation();
+
+ if (NULL == p->BlockingThread) {
+ //XXX.mjb: clean process
+ return STATUS_UNSUCCESSFUL;
+ }
+ Success = ResumeThread(p->BlockingThread);
+ ASSERT(Success);
+ Success = ResumeThread(p->Thread);
+ ASSERT(Success);
+
+ return STATUS_SUCCESS;
+}
+
+
+DWORD
+ForeignProcessWait(PVOID arg)
+{
+ PPSX_PROCESS p = arg;
+ ULONG ExitStatus;
+
+ WaitForSingleObject(p->Process, (DWORD)-1);
+
+ Exit(p, ExitStatus);
+
+ p->BlockingThread = NULL;
+
+ ExitThread(0);
+ //NOTREACHED
+ ASSERT(0);
+
+ return 0;
+}
+
+//
+// Change a posix-type path variable to win32 path. The given
+// buffer is modified in place.
+//
+VOID
+ConvertPathToWin(char *path)
+{
+ char *pch;
+
+ pch = path;
+
+ while (*path) {
+
+ // change ':' to ';'
+ if (':' == *path) {
+ *pch = ';';
+ ++path;
+ ++pch;
+ continue;
+ }
+
+ // change "//X" to "X:"
+ if ('/' == *path && '/' == path[1]) {
+ path += 2;
+ *pch = *path;
+ ++pch;
+ *pch = ':';
+
+ ++path;
+ ++pch;
+ continue;
+ }
+ // change slash to backslash
+ if ('/' == *path) {
+ *pch = '\\';
+ ++path;
+ ++pch;
+ continue;
+ }
+
+ *pch = *path;
+ ++pch;
+ ++path;
+ }
+ *pch = '\0';
+}
+
+#endif /* EXEC_FOREIGN */
diff --git a/private/posix/psxss/srvtc.c b/private/posix/psxss/srvtc.c
new file mode 100644
index 000000000..fb562c061
--- /dev/null
+++ b/private/posix/psxss/srvtc.c
@@ -0,0 +1,200 @@
+
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ tc.c
+
+Abstract:
+
+ Implementation of PSX termical control
+
+Author:
+
+ Ellen Aycock-Wright (ellena) 05-Aug-1991
+
+Revision History:
+
+--*/
+
+#include "psxsrv.h"
+
+
+BOOLEAN
+PsxTcGetAttr (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m
+ )
+{
+ PPSX_TCGETATTR_MSG args;
+ PFILEDESCRIPTOR Fd;
+
+ args = &m->u.TcGetAttr;
+
+ Fd = FdIndexToFd(p, args->FileDes);
+ if (!Fd) {
+ m->Error = EBADF;
+ return TRUE;
+ }
+ if (&ConVectors == Fd->SystemOpenFileDesc->IoNode->IoVectors) {
+ m->ReturnValue = 0;
+ return TRUE;
+ }
+
+ m->Error = ENOTTY;
+ return TRUE;
+}
+
+BOOLEAN
+PsxTcSetAttr (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m
+ )
+{
+ PPSX_TCSETATTR_MSG args;
+ PFILEDESCRIPTOR Fd;
+
+ args = &m->u.TcSetAttr;
+
+ Fd = FdIndexToFd(p, args->FileDes);
+ if (!Fd) {
+ m->Error = EBADF;
+ return TRUE;
+ }
+
+ if (&ConVectors == Fd->SystemOpenFileDesc->IoNode->IoVectors) {
+ m->ReturnValue = 0;
+ return TRUE;
+ }
+
+ m->Error = ENOTTY;
+ return TRUE;
+}
+
+BOOLEAN
+PsxTcSendBreak (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m
+ )
+{
+ PPSX_TCSENDBREAK_MSG args;
+ PFILEDESCRIPTOR Fd;
+
+ args = &m->u.TcSendBreak;
+
+ Fd = FdIndexToFd(p, args->FileDes);
+ if (!Fd) {
+ m->Error = EBADF;
+ return TRUE;
+ }
+
+ m->Error = ENOTTY;
+ return TRUE;
+}
+
+BOOLEAN
+PsxTcDrain (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m
+ )
+{
+ PPSX_TCDRAIN_MSG args;
+ PFILEDESCRIPTOR Fd;
+
+ args = &m->u.TcDrain;
+
+ Fd = FdIndexToFd(p, args->FileDes);
+ if (!Fd) {
+ m->Error = EBADF;
+ return TRUE;
+ }
+
+ m->Error = ENOTTY;
+ return TRUE;
+}
+
+BOOLEAN
+PsxTcFlush (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m
+ )
+{
+ PPSX_TCFLUSH_MSG args;
+ PFILEDESCRIPTOR Fd;
+
+ args = &m->u.TcFlush;
+
+ Fd = FdIndexToFd(p, args->FileDes);
+ if (!Fd) {
+ m->Error = EBADF;
+ return TRUE;
+ }
+
+ m->Error = ENOTTY;
+ return TRUE;
+}
+
+BOOLEAN
+PsxTcFlow (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m
+ )
+{
+ PPSX_TCFLOW_MSG args;
+ PFILEDESCRIPTOR Fd;
+
+ args = &m->u.TcFlow;
+
+ Fd = FdIndexToFd(p, args->FileDes);
+ if (!Fd) {
+ m->Error = EBADF;
+ return TRUE;
+ }
+
+ m->Error = ENOTTY;
+ return TRUE;
+}
+
+BOOLEAN
+PsxTcGetPGrp (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m
+ )
+{
+ PPSX_TCGETPGRP_MSG args;
+ PFILEDESCRIPTOR Fd;
+
+ args = &m->u.TcGetPGrp;
+
+ Fd = FdIndexToFd(p, args->FileDes);
+ if (!Fd) {
+ m->Error = EBADF;
+ return TRUE;
+ }
+
+ m->Error = ENOTTY;
+ return TRUE;
+}
+
+BOOLEAN
+PsxTcSetPGrp (
+ IN PPSX_PROCESS p,
+ IN OUT PPSX_API_MSG m
+ )
+{
+ PPSX_TCSETPGRP_MSG args;
+ PFILEDESCRIPTOR Fd;
+
+ args = &m->u.TcSetPGrp;
+
+ Fd = FdIndexToFd(p, args->FileDes);
+ if (!Fd) {
+ m->Error = EBADF;
+ return TRUE;
+ }
+
+ m->Error = ENOTTY;
+ return TRUE;
+}
diff --git a/private/posix/psxss/stub.c b/private/posix/psxss/stub.c
new file mode 100644
index 000000000..f1ef07d10
--- /dev/null
+++ b/private/posix/psxss/stub.c
@@ -0,0 +1,100 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ stub.c
+
+Abstract:
+
+ Stubs
+
+Author:
+
+ Mark Lucovsky (markl) 30-Mar-1989
+
+Revision History:
+
+--*/
+
+
+#include "psxsrv.h"
+
+VOID
+Panic(
+ IN PSZ PanicString
+ )
+{
+ KdPrint(("Panic: %s\n", PanicString));
+ DbgBreakPoint();
+ for(;;);
+}
+
+
+
+//
+// Api Stubs
+//
+
+
+BOOLEAN
+PsxSetUid(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+{
+ m->Error = ENOSYS;
+ return TRUE;
+}
+
+BOOLEAN
+PsxSetGid(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+{
+ m->Error = ENOSYS;
+ return TRUE;
+}
+
+BOOLEAN
+PsxCUserId(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+{
+ m->Error = ENOSYS;
+ return TRUE;
+}
+
+
+BOOLEAN
+PsxUname(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+{
+ m->Error = ENOSYS;
+ return TRUE;
+}
+
+BOOLEAN
+PsxTime(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+{
+ m->Error = ENOSYS;
+ return TRUE;
+}
+
+BOOLEAN
+PsxTtyName(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+{
+ m->Error = ENOTTY;
+ return TRUE;
+}
diff --git a/private/posix/psxss/sysdb.c b/private/posix/psxss/sysdb.c
new file mode 100644
index 000000000..1b608bdd3
--- /dev/null
+++ b/private/posix/psxss/sysdb.c
@@ -0,0 +1,1258 @@
+/*--
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ sysdb.c
+
+Abstract:
+
+ System database access routines.
+
+Author:
+
+ Matthew Bradburn (mattbr) 04-Mar-1992
+
+Revision History:
+
+--*/
+
+#include "psxsrv.h"
+#include "psxmsg.h"
+#include <pwd.h>
+#include <grp.h>
+#include <ntsam.h>
+#include <ntlsa.h>
+#include <seposix.h>
+#include <ctype.h>
+
+#define UNICODE
+#include <windef.h>
+#include <winbase.h>
+
+static NTSTATUS
+PutUserInfo(
+ PSID DomainSid,
+ SAM_HANDLE DomainHandle,
+ ULONG UserId,
+ PCHAR DataDest,
+ OUT int *pLength
+ );
+
+static VOID
+PutSpecialUserInfo(
+ PUNICODE_STRING Name,
+ PCHAR DataDest,
+ uid_t Uid,
+ OUT int *pLength
+ );
+
+static NTSTATUS
+PutGroupInfo(
+ PSID DomainSid,
+ SAM_HANDLE DomainHandle,
+ ULONG GroupId,
+ PCHAR DataDest,
+ IN SID_NAME_USE Type,
+ OUT int *pLength
+ );
+
+static VOID
+PutSpecialGroupInfo(
+ PUNICODE_STRING Name,
+ PCHAR DataDest,
+ gid_t Gid,
+ OUT int *pLength
+ );
+
+static BOOLEAN
+ConvertPathToPsx(
+ ANSI_STRING *A
+ );
+
+PSID
+GetSpecialSid(
+ uid_t Uid
+ );
+
+NTSTATUS
+MySamConnect(
+ IN PSID DomainSid, // NULL if domain unknown
+ OUT PSAM_HANDLE ServerHandle
+ )
+{
+ SECURITY_QUALITY_OF_SERVICE
+ SecurityQoS;
+ OBJECT_ATTRIBUTES
+ Obj;
+ NTSTATUS
+ Status;
+ LSA_HANDLE
+ TrustedDomainHandle,
+ PolicyHandle;
+ PTRUSTED_CONTROLLERS_INFO
+ pBuf;
+ ULONG i;
+
+ SecurityQoS.ImpersonationLevel = SecurityIdentification;
+ SecurityQoS.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ SecurityQoS.EffectiveOnly = TRUE;
+
+ InitializeObjectAttributes(&Obj, NULL, 0, NULL, NULL);
+ Obj.SecurityQualityOfService = &SecurityQoS;
+
+ if (NULL == DomainSid) {
+ Status = SamConnect(NULL, ServerHandle,
+ GENERIC_READ | GENERIC_EXECUTE, &Obj);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: Can't connect to SAM: 0x%x\n",
+ Status));
+ }
+ return Status;
+ }
+
+ Status = LsaOpenPolicy(NULL, &Obj, GENERIC_EXECUTE, &PolicyHandle);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: MySamConnect: LsaOpenPolicy: 0x%x\n", Status));
+ return Status;
+ }
+ Status = LsaOpenTrustedDomain(PolicyHandle, DomainSid,
+ GENERIC_READ | GENERIC_EXECUTE,
+ &TrustedDomainHandle);
+ if (!NT_SUCCESS(Status)) {
+ LsaClose(PolicyHandle);
+
+ Status = SamConnect(NULL, ServerHandle,
+ GENERIC_READ | GENERIC_EXECUTE, &Obj);
+
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: Can't connect to SAM: 0x%x\n",
+ Status));
+ }
+ return Status;
+ }
+ Status = LsaQueryInfoTrustedDomain(TrustedDomainHandle,
+ TrustedControllersInformation, (PVOID *)&pBuf);
+ LsaClose(PolicyHandle);
+ LsaClose(TrustedDomainHandle);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: MySamConnect: LsaQueryInfoTrusted: 0x%x\n",
+ Status));
+ return Status;
+ }
+ for (i = 0; i < pBuf->Entries; ++i) {
+ if (0 == pBuf->Names[i].Length) {
+ // the null string signifies domain controller unknown
+ continue;
+ }
+ Status = SamConnect(&pBuf->Names[i], ServerHandle,
+ GENERIC_READ | GENERIC_EXECUTE, &Obj);
+ if (NT_SUCCESS(Status)) {
+ // found an acceptable choice
+ LsaFreeMemory(pBuf);
+ return Status;
+ }
+ }
+ LsaFreeMemory(pBuf);
+
+ //
+ // If there were no acceptable domain controllers, we try the
+ // machine domain
+ //
+
+ Status = SamConnect(NULL, ServerHandle, GENERIC_EXECUTE, &Obj);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: Can't connect to SAM: 0x%x\n", Status));
+ }
+ return Status;
+}
+
+NTSTATUS
+GetMyAccountDomainName(
+ PUNICODE_STRING Domain_U
+ )
+{
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES Obj;
+ SECURITY_QUALITY_OF_SERVICE SecurityQoS;
+ LSA_HANDLE PolicyHandle;
+ PPOLICY_ACCOUNT_DOMAIN_INFO AccountDomainInfo;
+
+ SecurityQoS.ImpersonationLevel = SecurityIdentification;
+ SecurityQoS.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ SecurityQoS.EffectiveOnly = TRUE;
+
+ InitializeObjectAttributes(&Obj, NULL, 0, NULL, NULL);
+ Obj.SecurityQualityOfService = &SecurityQoS;
+
+ Status = LsaOpenPolicy(NULL, &Obj, GENERIC_EXECUTE, &PolicyHandle);
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+ Status = LsaQueryInformationPolicy(PolicyHandle,
+ PolicyAccountDomainInformation, (PVOID *)&AccountDomainInfo);
+ if (!NT_SUCCESS(Status)) {
+ LsaClose(PolicyHandle);
+ return Status;
+ }
+
+ LsaClose(PolicyHandle);
+
+ Domain_U->MaximumLength = AccountDomainInfo->DomainName.MaximumLength;
+ Domain_U->Buffer = RtlAllocateHeap(PsxHeap, 0,
+ Domain_U->MaximumLength);
+ if (NULL == Domain_U->Buffer) {
+ return STATUS_NO_MEMORY;
+ }
+
+ RtlCopyUnicodeString(Domain_U, &AccountDomainInfo->DomainName);
+
+ LsaFreeMemory(AccountDomainInfo);
+
+ return STATUS_SUCCESS;
+}
+
+BOOLEAN
+PsxGetPwUid(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+{
+ NTSTATUS Status;
+ PPSX_GETPWUID_MSG args;
+ PSID DomainSid;
+ SAM_HANDLE
+ ServerHandle = NULL,
+ DomainHandle = NULL;
+ LSA_HANDLE
+ PolicyHandle = NULL;
+
+ PSID Sid = NULL;
+ OBJECT_ATTRIBUTES Obj;
+ SECURITY_QUALITY_OF_SERVICE SecurityQoS;
+ PLSA_REFERENCED_DOMAIN_LIST ReferencedDomains;
+ PLSA_TRANSLATED_NAME Names;
+
+ args = &m->u.GetPwUid;
+
+ if (SE_NULL_POSIX_ID == (args->Uid & 0xFFFF0000)) {
+ // Special case for universal well-known sids and nt
+ // ... well-known sids.
+
+ Sid = GetSpecialSid(args->Uid);
+ if (NULL == Sid) {
+ m->Error = 1;
+ return TRUE;
+ }
+
+ goto TryLsa;
+ }
+
+ DomainSid = GetSidByOffset(args->Uid & 0xFFFF0000);
+ if (NULL == DomainSid) {
+ m->Error = 1;
+ return TRUE;
+ }
+
+ Status = MySamConnect(DomainSid, &ServerHandle);
+ if (!NT_SUCCESS(Status)) {
+ m->Error = 1;
+ return TRUE;
+ }
+
+ Status = SamOpenDomain(ServerHandle, GENERIC_EXECUTE, DomainSid,
+ &DomainHandle);
+ if (NT_SUCCESS(Status)) {
+ Status = PutUserInfo(DomainSid, DomainHandle,
+ args->Uid & 0xFFFF,
+ (PCHAR)args->PwBuf, &args->Length);
+ if (NT_SUCCESS(Status)) {
+ goto out;
+ }
+ }
+
+ //
+ // SAM can't find the name, so we'll try the LSA.
+ //
+
+
+ Sid = MakeSid(DomainSid, args->Uid & 0x0000FFFF);
+
+TryLsa:
+ SecurityQoS.ImpersonationLevel = SecurityIdentification;
+ SecurityQoS.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ SecurityQoS.EffectiveOnly = TRUE;
+
+ InitializeObjectAttributes(&Obj, NULL, 0, NULL, NULL);
+ Obj.SecurityQualityOfService = &SecurityQoS;
+
+ Status = LsaOpenPolicy(NULL, &Obj, GENERIC_EXECUTE, &PolicyHandle);
+ if (!NT_SUCCESS(Status)) {
+ m->Error = PsxStatusToErrno(Status);
+ goto out;
+ }
+
+ Status = LsaLookupSids(PolicyHandle, 1, &Sid, &ReferencedDomains,
+ &Names);
+ if (NT_SUCCESS(Status)) {
+ LsaFreeMemory((PVOID)ReferencedDomains);
+ PutSpecialUserInfo(&Names->Name, (PCHAR)args->PwBuf,
+ args->Uid, &args->Length);
+ LsaFreeMemory((PVOID)Names);
+ } else {
+ UNICODE_STRING U;
+ RtlConvertSidToUnicodeString(&U, Sid, TRUE);
+ PutSpecialUserInfo(&U, (PCHAR)args->PwBuf, args->Uid,
+ &args->Length);
+ RtlFreeUnicodeString(&U);
+ }
+
+out:
+ if (NULL != Sid) RtlFreeHeap(PsxHeap, 0, (PVOID)Sid);
+ if (NULL != ServerHandle) SamCloseHandle(ServerHandle);
+ if (NULL != DomainHandle) SamCloseHandle(DomainHandle);
+ if (NULL != PolicyHandle) LsaClose(PolicyHandle);
+ return TRUE;
+}
+
+BOOLEAN
+PsxGetPwNam(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+{
+ PPSX_GETPWNAM_MSG args;
+ NTSTATUS Status;
+ UNICODE_STRING
+ Name_U, // the user's name in unicode
+ Domain_U; // the name of the account domain
+ ANSI_STRING
+ Name_A; // the user's name in Ansi
+ PULONG pUserId = NULL; // the relative offset for the user
+ SAM_HANDLE
+ ServerHandle = NULL,
+ DomainHandle = NULL;
+ PSID_NAME_USE
+ pUse;
+ PSID DomainSid = NULL;
+ WCHAR ComputerNameBuf[32 + 1];
+ ULONG Len = 32 + 1;
+
+ Name_U.Buffer = NULL;
+
+ args = &m->u.GetPwNam;
+
+ Status = MySamConnect(DomainSid, &ServerHandle);
+ if (!NT_SUCCESS(Status)) {
+ m->Error = 1;
+ return TRUE;
+ }
+
+ //
+ // Find the name of the local machine -- we look there for
+ // the name being requested.
+ //
+
+ if (!GetComputerName(ComputerNameBuf, &Len)) {
+ KdPrint(("PSXSS: GetComputerName: 0x%x\n", GetLastError()));
+ m->Error = 1;
+ goto out;
+ }
+
+ RtlInitUnicodeString(&Domain_U, ComputerNameBuf);
+ Status = SamLookupDomainInSamServer(ServerHandle, &Domain_U,
+ &DomainSid);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: Can't lookup domain: 0x%x\n", Status));
+
+ //
+ // If the "machinename" domain didn't work, try the
+ // account domain.
+ //
+
+ Status = GetMyAccountDomainName(&Domain_U);
+ if (!NT_SUCCESS(Status)) {
+ m->Error = 1;
+ goto out;
+ }
+
+ Status = SamLookupDomainInSamServer(ServerHandle, &Domain_U,
+ &DomainSid);
+
+ RtlFreeHeap(PsxHeap, 0, (PVOID)Domain_U.Buffer);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: Can't lookup acct domain: 0x%x\n", Status));
+ m->Error = 1;
+ goto out;
+ }
+ }
+
+ Status = SamOpenDomain(ServerHandle, GENERIC_EXECUTE, DomainSid,
+ &DomainHandle);
+ if (!NT_SUCCESS(Status)) {
+ m->Error = 1;
+ goto out;
+ }
+
+ RtlInitAnsiString(&Name_A, args->Name);
+ Status = RtlAnsiStringToUnicodeString(&Name_U, &Name_A, TRUE);
+ if (!NT_SUCCESS(Status)) {
+ m->Error = ENOMEM;
+ goto out;
+ }
+
+ Status = SamLookupNamesInDomain(DomainHandle, 1, &Name_U, &pUserId,
+ &pUse);
+ if (!NT_SUCCESS(Status)) {
+ m->Error = ENOENT;
+ goto out;
+ }
+
+ // Make sure the name is a user name.
+ if (*pUse != SidTypeUser) {
+ SamFreeMemory(pUse);
+ KdPrint(("PSXSS: Group name is type %d\n", *pUse));
+ m->Error = 1;
+ goto out;
+ }
+ SamFreeMemory(pUse);
+
+ Status = PutUserInfo(DomainSid, DomainHandle, *pUserId,
+ (PCHAR)args->PwBuf, &args->Length);
+ if (!NT_SUCCESS(Status)) {
+ m->Error = 1;
+ }
+out:
+ if (NULL != ServerHandle) {
+ SamCloseHandle(ServerHandle);
+ }
+ if (NULL != DomainHandle) {
+ SamCloseHandle(DomainHandle);
+ }
+ if (NULL != Name_U.Buffer) {
+ RtlFreeUnicodeString(&Name_U);
+ }
+ if (NULL != pUserId) {
+ SamFreeMemory(pUserId);
+ }
+ if (NULL != DomainSid) {
+ SamFreeMemory(DomainSid);
+ }
+ return TRUE;
+}
+
+static VOID
+PutSpecialUserInfo(
+ PUNICODE_STRING Name,
+ PCHAR DataDest,
+ uid_t Uid,
+ OUT int *pLength
+ )
+{
+ PCHAR pch;
+ struct passwd *pwd;
+ ANSI_STRING A;
+
+ pwd = (struct passwd *)DataDest;
+ pwd->pw_uid = Uid;
+
+
+ // Don't know what the gid should be, we use the uid.
+ pwd->pw_gid = Uid;
+
+ pch = DataDest + sizeof(struct passwd);
+ pwd->pw_name = pch - (ULONG)DataDest;
+ A.Buffer = pch;
+ A.MaximumLength = NAME_MAX;
+ RtlUnicodeStringToAnsiString(&A, Name, FALSE);
+
+ pch += strlen(pch) + 1;
+ pwd->pw_dir = pch - (ULONG)DataDest;
+ strcpy(pch, "/");
+
+ pch += strlen(pch) + 1;
+ pwd->pw_shell = pch - (ULONG)DataDest;
+ strcpy(pch, "noshell");
+
+ pch += strlen(pch) + 1;
+ *pLength = (ULONG)pch - (ULONG)DataDest;
+}
+
+//
+// PutUserInfo -- place password database information about the user at
+// the specified data destination address.
+//
+static NTSTATUS
+PutUserInfo(
+ PSID DomainSid,
+ SAM_HANDLE DomainHandle,
+ ULONG UserId,
+ PCHAR DataDest,
+ int *pLength
+ )
+{
+ SAM_HANDLE
+ UserHandle = NULL;
+ PUSER_ACCOUNT_INFORMATION
+ AccountInfo = NULL;
+ PSID Sid; // User Sid
+ ULONG SpaceLeft;
+ struct passwd *pwd;
+ PCHAR pch;
+ NTSTATUS Status;
+ ANSI_STRING A;
+ PCHAR pchPsxDir;
+
+ Status = SamOpenUser(DomainHandle, GENERIC_READ | GENERIC_EXECUTE,
+ UserId, &UserHandle);
+ if (!NT_SUCCESS(Status)) {
+ goto out;
+ }
+
+ Status = SamQueryInformationUser(UserHandle, UserAccountInformation,
+ (PVOID *)&AccountInfo);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: Can't query info user: 0x%x\n", Status));
+ goto out;
+ }
+
+ pwd = (struct passwd *)DataDest;
+ Sid = MakeSid(DomainSid, AccountInfo->UserId);
+ if (NULL == Sid) {
+ goto out;
+ }
+ pwd->pw_uid = MakePosixId(Sid);
+
+ RtlFreeHeap(PsxHeap, 0, (PVOID)Sid);
+
+ Sid = MakeSid(DomainSid, AccountInfo->PrimaryGroupId);
+ if (NULL == Sid) {
+ goto out;
+ }
+ pwd->pw_gid = MakePosixId(Sid);
+
+ RtlFreeHeap(PsxHeap, 0, (PVOID)Sid);
+
+ SpaceLeft = ARG_MAX - sizeof(struct passwd);
+
+ pch = DataDest + sizeof(struct passwd);
+ pwd->pw_name = pch - (ULONG)DataDest;
+ A.Buffer = pch;
+ A.MaximumLength = (USHORT)SpaceLeft;
+ Status = RtlUnicodeStringToAnsiString(&A, &AccountInfo->UserName,
+ FALSE);
+ if (!NT_SUCCESS(Status)) {
+ goto out;
+ }
+ SpaceLeft -= A.Length;
+
+ pch = pch + A.Length + 1;
+ pwd->pw_dir = pch - (ULONG)DataDest;
+ A.Buffer = pch;
+ A.MaximumLength = (USHORT)SpaceLeft;
+
+ Status = RtlUnicodeStringToAnsiString(&A, &AccountInfo->HomeDirectory,
+ FALSE);
+ if (!NT_SUCCESS(Status)) {
+ goto out;
+ }
+ if (!ConvertPathToPsx(&A)) {
+ goto out;
+ }
+ SpaceLeft -= A.Length;
+
+ if (SpaceLeft <= strlen("noshell")) {
+ goto out;
+ }
+
+ pch = pch + A.Length + 1;
+ pwd->pw_shell = pch - (ULONG)DataDest;
+ strcpy(pch, "noshell");
+
+ pch += strlen(pch) + 1;
+
+ *pLength = (ULONG)pch - (ULONG)DataDest;
+
+out:
+ SamCloseHandle(UserHandle);
+
+ if (NULL != UserHandle) {
+ SamCloseHandle(UserHandle);
+ }
+ if (NULL != AccountInfo) {
+ SamFreeMemory(AccountInfo);
+ }
+ return Status;
+}
+
+BOOLEAN
+PsxGetGrGid(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+{
+ NTSTATUS Status;
+ PPSX_GETGRGID_MSG args;
+ ULONG Gid;
+ PSID DomainSid, GroupSid;
+ SAM_HANDLE
+ ServerHandle = NULL,
+ DomainHandle = NULL;
+ PLSA_REFERENCED_DOMAIN_LIST
+ ReferencedDomains = NULL;
+ PLSA_TRANSLATED_NAME
+ Names = NULL;
+ LSA_HANDLE PolicyHandle = NULL;
+
+ OBJECT_ATTRIBUTES Obj;
+ SECURITY_QUALITY_OF_SERVICE SecurityQoS;
+ PSID Sid = NULL;
+
+ args = &m->u.GetGrGid;
+ Gid = args->Gid;
+
+ if (0xFFF == Gid) {
+ UNICODE_STRING U;
+
+ //
+ // This is a login group, which we'll just translate to
+ // S-1-5-5-0-0
+ //
+
+ RtlInitUnicodeString(&U, L"S-1-5-5-0-0");
+ PutSpecialGroupInfo(&U, (PCHAR)args->GrBuf, Gid,
+ &args->Length);
+ return TRUE;
+ }
+
+ if (SE_NULL_POSIX_ID == (Gid & 0xFFFF0000)) {
+ // Special case for universal well-known sids and nt
+ // ... well-known sids.
+
+ Sid = GetSpecialSid(Gid);
+ if (NULL == Sid) {
+ m->Error = 1;
+ return TRUE;
+ }
+
+ goto TryLsa;
+ }
+
+ DomainSid = GetSidByOffset(Gid & 0xFFFF0000);
+ if (NULL == DomainSid) {
+ m->Error = 1;
+ return TRUE;
+ }
+
+
+ Status = MySamConnect(DomainSid, &ServerHandle);
+ if (!NT_SUCCESS(Status)) {
+ m->Error = 1;
+ return TRUE;
+ }
+
+ Status = SamOpenDomain(ServerHandle, GENERIC_READ | GENERIC_EXECUTE,
+ DomainSid, &DomainHandle);
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Look for a group
+ //
+
+ Status = PutGroupInfo(DomainSid, DomainHandle, Gid & 0xFFFF,
+ (PCHAR)args->GrBuf, SidTypeGroup,
+ &args->Length);
+ if (NT_SUCCESS(Status)) {
+ goto out;
+ }
+
+ //
+ // Try again, except look for an alias
+ //
+
+ Status = PutGroupInfo(DomainSid, DomainHandle, Gid & 0xFFFF,
+ (PCHAR)args->GrBuf, SidTypeAlias, &args->Length);
+
+ if (NT_SUCCESS(Status)) {
+ goto out;
+ }
+ }
+
+ //
+ // Give up looking in SAM, ask LSA to identify.
+ //
+
+ Sid = MakeSid(DomainSid, Gid & 0x0000FFFF);
+
+TryLsa:
+ SecurityQoS.ImpersonationLevel = SecurityIdentification;
+ SecurityQoS.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ SecurityQoS.EffectiveOnly = TRUE;
+
+ InitializeObjectAttributes(&Obj, NULL, 0, NULL, NULL);
+ Obj.SecurityQualityOfService = &SecurityQoS;
+
+ Status = LsaOpenPolicy(NULL, &Obj, GENERIC_EXECUTE, &PolicyHandle);
+ if (!NT_SUCCESS(Status)) {
+ m->Error = PsxStatusToErrno(Status);
+ goto out;
+ }
+
+ Status = LsaLookupSids(PolicyHandle, 1, &Sid, &ReferencedDomains,
+ &Names);
+ if (NT_SUCCESS(Status)) {
+ LsaFreeMemory((PVOID)ReferencedDomains);
+ PutSpecialGroupInfo(&Names->Name, (PCHAR)args->GrBuf, Gid,
+ &args->Length);
+ LsaFreeMemory((PVOID)Names);
+ goto out;
+ } else {
+ UNICODE_STRING U;
+ RtlConvertSidToUnicodeString(&U, Sid, TRUE);
+ PutSpecialGroupInfo(&U, (PCHAR)args->GrBuf, Gid,
+ &args->Length);
+ RtlFreeUnicodeString(&U);
+ goto out;
+ }
+
+out:
+ if (NULL != PolicyHandle) LsaClose(PolicyHandle);
+ if (NULL != ServerHandle) SamCloseHandle(ServerHandle);
+ if (NULL != DomainHandle) SamCloseHandle(DomainHandle);
+ if (NULL != Sid) RtlFreeHeap(PsxHeap, 0, (PVOID)Sid);
+ return TRUE;
+
+}
+
+BOOLEAN
+PsxGetGrNam(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+{
+ PPSX_GETGRNAM_MSG args;
+ NTSTATUS Status;
+ UNICODE_STRING
+ Name_U, // the group name in unicode
+ Domain_U; // the name of the account domain
+ ANSI_STRING
+ Name_A; // the group name in Ansi
+ PULONG pGroupId = NULL; // the relative offset for the group
+ SAM_HANDLE
+ ServerHandle = NULL,
+ DomainHandle = NULL;
+ PSID_NAME_USE
+ pUse;
+ PSID DomainSid = NULL,
+ Sid = NULL;
+ WCHAR ComputerNameBuf[32 + 1];
+ ULONG Len = 32 + 1;
+ SID_NAME_USE Type;
+
+ Name_U.Buffer = NULL;
+ args = &m->u.GetGrNam;
+
+ Status = MySamConnect(DomainSid, &ServerHandle);
+ if (!NT_SUCCESS(Status)) {
+ m->Error = 1;
+ return TRUE;
+ }
+
+ //
+ // Find the name of the local machine -- we look here for
+ // the name being requested.
+ //
+
+ if (!GetComputerName(ComputerNameBuf, &Len)) {
+ KdPrint(("PSXSS: GetComputerName: 0x%x\n", GetLastError()));
+ m->Error = 1;
+ goto out;
+ }
+
+ RtlInitUnicodeString(&Domain_U, ComputerNameBuf);
+
+ Status = SamLookupDomainInSamServer(ServerHandle, &Domain_U,
+ &DomainSid);
+ if (!NT_SUCCESS(Status)) {
+ //
+ // If the "machinename" domain didn't work, try the
+ // account domain.
+ //
+
+ Status = GetMyAccountDomainName(&Domain_U);
+ if (!NT_SUCCESS(Status)) {
+ m->Error = 1;
+ goto out;
+ }
+
+ Status = SamLookupDomainInSamServer(ServerHandle, &Domain_U,
+ &DomainSid);
+ RtlFreeHeap(PsxHeap, 0, (PVOID)Domain_U.Buffer);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: Can't lookup acct domain: 0x%x\n", Status));
+ m->Error = 1;
+ goto out;
+ }
+ }
+
+ Status = SamOpenDomain(ServerHandle, GENERIC_READ | GENERIC_EXECUTE,
+ DomainSid, &DomainHandle);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: Can't open domain: 0x%x\n", Status));
+ m->Error = 1;
+ goto out;
+ }
+
+ RtlInitAnsiString(&Name_A, args->Name);
+ Status = RtlAnsiStringToUnicodeString(&Name_U, &Name_A, TRUE);
+ if (!NT_SUCCESS(Status)) {
+ m->Error = ENOMEM;
+ goto out;
+ }
+
+ Status = SamLookupNamesInDomain(DomainHandle, 1, &Name_U, &pGroupId,
+ &pUse);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: Can't lookup name: 0x%x\n", Status));
+ m->Error = 1;
+ goto out;
+ }
+
+ // Make sure the name is a group name.
+ Type = *pUse;
+
+ SamFreeMemory(pUse);
+ if (Type != SidTypeGroup && Type != SidTypeAlias) {
+ m->Error = EINVAL;
+ goto out;
+ }
+
+ Status = PutGroupInfo(DomainSid, DomainHandle, *pGroupId,
+ (PCHAR)args->GrBuf, Type, &args->Length);
+
+out:
+ if (NULL != ServerHandle) {
+ SamCloseHandle(ServerHandle);
+ }
+ if (NULL != DomainHandle) {
+ SamCloseHandle(DomainHandle);
+ }
+ if (NULL != Name_U.Buffer) {
+ RtlFreeUnicodeString(&Name_U);
+ }
+ if (NULL != pGroupId) {
+ SamFreeMemory(pGroupId);
+ }
+ if (NULL != DomainSid) {
+ SamFreeMemory(DomainSid);
+ }
+
+ return TRUE;
+}
+
+static VOID
+PutSpecialGroupInfo(
+ PUNICODE_STRING Name,
+ PCHAR DataDest,
+ gid_t Gid,
+ OUT int *pLength
+ )
+{
+ struct group *grp;
+ PCHAR pch, *ppchMem;
+ ANSI_STRING A;
+
+ //
+ // The struct group goes at the beginning of the view memory,
+ // followed by the array of member name pointers.
+ //
+ grp = (struct group *)DataDest;
+ ppchMem = (PCHAR *)((PCHAR)DataDest + sizeof(struct group));
+
+ grp->gr_mem = (PCHAR *)((PCHAR)ppchMem - (ULONG)DataDest);
+
+ grp->gr_gid = Gid;
+
+ // No members.
+
+ ppchMem[0] = NULL;
+
+ pch = (PCHAR)(ppchMem + 1);
+ grp->gr_name = pch - (ULONG)DataDest;
+ A.Buffer = pch;
+ A.MaximumLength = NAME_MAX;
+ RtlUnicodeStringToAnsiString(&A, Name, FALSE);
+
+ pch += strlen(pch) + 1;
+ *pLength = (ULONG)(pch - (ULONG)DataDest);
+}
+
+NTSTATUS
+GetUserNameFromSid(
+ PSID Sid,
+ PANSI_STRING pA)
+{
+ ULONG SubAuthCount;
+ ULONG RelativeId;
+ PSID DomainSid = NULL;
+ SAM_HANDLE
+ ServerHandle = NULL,
+ DomainHandle = NULL,
+ UserHandle = NULL;
+ PUSER_ACCOUNT_INFORMATION
+ AccountInfo = NULL;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ SubAuthCount = *RtlSubAuthorityCountSid(Sid);
+ RelativeId = *RtlSubAuthoritySid(Sid, SubAuthCount - 1);
+
+ DomainSid = RtlAllocateHeap(PsxHeap, 0, RtlLengthSid(Sid));
+ if (NULL == DomainSid) {
+ goto out;
+ }
+ Status = RtlCopySid(RtlLengthSid(Sid), DomainSid, Sid);
+ ASSERT(NT_SUCCESS(Status));
+
+ --*RtlSubAuthorityCountSid(DomainSid);
+
+ Status = MySamConnect(DomainSid, &ServerHandle);
+ if (!NT_SUCCESS(Status)) {
+ goto out;
+ }
+
+ Status = SamOpenDomain(ServerHandle, GENERIC_EXECUTE,
+ DomainSid, &DomainHandle);
+ if (!NT_SUCCESS(Status)) {
+ goto out;
+ }
+
+ Status = SamOpenUser(DomainHandle, GENERIC_READ | GENERIC_EXECUTE,
+ RelativeId, &UserHandle);
+ if (!NT_SUCCESS(Status)) {
+ goto out;
+ }
+
+ Status = SamQueryInformationUser(UserHandle, UserAccountInformation,
+ (PVOID *)&AccountInfo);
+ if (!NT_SUCCESS(Status)) {
+ goto out;
+ }
+ Status = RtlUnicodeStringToAnsiString(pA, &AccountInfo->UserName, FALSE);
+
+out:
+ if (NULL != DomainSid) RtlFreeHeap(PsxHeap, 0, DomainSid);
+ if (NULL != ServerHandle) SamCloseHandle(ServerHandle);
+ if (NULL != DomainHandle) SamCloseHandle(DomainHandle);
+ if (NULL != UserHandle) SamCloseHandle(UserHandle);
+ if (NULL != AccountInfo) SamFreeMemory(AccountInfo);
+
+ return Status;
+}
+
+static NTSTATUS
+PutGroupInfo(
+ PSID DomainSid,
+ SAM_HANDLE DomainHandle,
+ ULONG GroupId,
+ PCHAR DataDest,
+ IN SID_NAME_USE Type,
+ OUT int *pLength
+ )
+{
+ ANSI_STRING
+ A; // misc. ansi strings
+ PGROUP_NAME_INFORMATION
+ NameInfo = NULL;
+ ULONG SpaceLeft,
+ count,
+ i;
+ struct group *grp;
+ PCHAR pch, *ppchMem;
+ PSID Sid;
+ NTSTATUS Status;
+ SAM_HANDLE GroupHandle = NULL;
+ PULONG
+ puGroupMem = NULL, // array of group members' relative id's
+ puAttr = NULL; // array of group members' attributes
+ PSID
+ *ppsGroupMem = NULL; // array of alias members' relative id's
+
+ //
+ // The struct group goes at the beginning of the view memory,
+ // followed by the array of member name pointers.
+ //
+ grp = (struct group *)DataDest;
+ ppchMem = (PCHAR *)((PCHAR)DataDest + sizeof(struct group));
+
+ grp->gr_mem = (PCHAR *)((PCHAR)ppchMem - (ULONG)DataDest);
+
+ Sid = MakeSid(DomainSid, GroupId);
+ if (NULL == Sid) {
+ goto out;
+ }
+ grp->gr_gid = MakePosixId(Sid);
+ RtlFreeHeap(PsxHeap, 0, Sid);
+
+ if (SidTypeGroup == Type) {
+ Status = SamOpenGroup(DomainHandle,
+ GENERIC_READ | GENERIC_EXECUTE,
+ GroupId, &GroupHandle);
+ if (!NT_SUCCESS(Status)) {
+ goto out;
+ }
+ Status = SamGetMembersInGroup(GroupHandle, &puGroupMem,
+ &puAttr, &count);
+ if (!NT_SUCCESS(Status)) {
+ goto out;
+ }
+ Status = SamQueryInformationGroup(GroupHandle,
+ GroupNameInformation, (PVOID *)&NameInfo);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: Can't query info group: 0x%x\n",
+ Status));
+ goto out;
+ }
+ } else {
+ Status = SamOpenAlias(DomainHandle,
+ GENERIC_READ | GENERIC_EXECUTE,
+ GroupId, &GroupHandle);
+ if (!NT_SUCCESS(Status)) {
+ goto out;
+ }
+ Status = SamGetMembersInAlias(GroupHandle, &ppsGroupMem,
+ &count);
+ if (!NT_SUCCESS(Status)) {
+ goto out;
+ }
+ Status = SamQueryInformationAlias(GroupHandle,
+ AliasNameInformation, (PVOID *)&NameInfo);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: Can't query info alias: 0x%x\n",
+ Status));
+ goto out;
+ }
+ }
+
+
+ //
+ // The strings start after the member name pointer array. We leave
+ // an extra member name pointer for the null-terminator.
+ //
+
+ pch = (PCHAR)(ppchMem + 1 + count);
+
+ SpaceLeft = PSX_CLIENT_PORT_MEMORY_SIZE -
+ (ULONG)(pch - (ULONG)DataDest);
+ grp->gr_name = pch - (ULONG)DataDest;
+ A.Buffer = pch;
+ A.MaximumLength = (USHORT)SpaceLeft;
+ Status = RtlUnicodeStringToAnsiString(&A, &NameInfo->Name, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ goto out;
+ }
+ SpaceLeft -= A.Length;
+ pch = pch + A.Length + 1;
+
+ for (i = 0; i < count; ++i) {
+
+ SAM_HANDLE UserHandle;
+ PUSER_ACCOUNT_NAME_INFORMATION pUserInfo;
+
+ ppchMem[i] = pch - (ULONG)DataDest;
+ A.Buffer = pch;
+ A.MaximumLength = (USHORT)SpaceLeft;
+
+ if (Type == SidTypeGroup) {
+ Status = SamOpenUser(DomainHandle,
+ GENERIC_READ | GENERIC_EXECUTE,
+ puGroupMem[i], &UserHandle);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: SamOpenUser: 0x%x\n", Status));
+ continue;
+ }
+
+ Status = SamQueryInformationUser(UserHandle,
+ UserAccountNameInformation,
+ (PVOID *)&pUserInfo);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: SamQueryInfoUser: 0x%x\n",
+ Status));
+ }
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = SamCloseHandle(UserHandle);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: SamCloseHandle: 0x%x\n",
+ Status));
+ }
+ ASSERT(NT_SUCCESS(Status));
+
+ RtlUnicodeStringToAnsiString(&A,
+ &pUserInfo->UserName, FALSE);
+
+ SamFreeMemory(pUserInfo);
+ } else {
+ Status = GetUserNameFromSid(ppsGroupMem[i], &A);
+ if (!NT_SUCCESS(Status)) {
+ continue;
+ }
+ }
+
+ SpaceLeft -= A.Length;
+ pch = pch + A.Length + 1;
+
+ }
+
+ ppchMem[i] = NULL;
+ *pLength = (ULONG)(pch - (ULONG)DataDest);
+
+ SamCloseHandle(GroupHandle);
+
+ return STATUS_SUCCESS;
+
+out:
+ if (NULL != GroupHandle) {
+ SamCloseHandle(GroupHandle);
+ }
+ if (NULL != puGroupMem) {
+ SamFreeMemory(puGroupMem);
+ }
+ if (NULL != ppsGroupMem) {
+ SamFreeMemory(ppsGroupMem);
+ }
+ if (NULL != puAttr) {
+ SamFreeMemory(puAttr);
+ }
+ if (NULL != NameInfo) {
+ SamFreeMemory(NameInfo);
+ }
+ return STATUS_BUFFER_TOO_SMALL;
+}
+
+//
+// MakeSid -- Attach the given relative id to the given Domain Sid to
+// make a new Sid, and return it. That new sid must be freed with
+// RtlFreeHeap.
+//
+PSID
+MakeSid(
+ PSID DomainSid,
+ ULONG RelativeId
+ )
+{
+ PSID NewSid;
+ NTSTATUS Status;
+ UCHAR AuthCount;
+ int i;
+
+ AuthCount = *RtlSubAuthorityCountSid(DomainSid) + 1;
+
+ NewSid = RtlAllocateHeap(PsxHeap, 0, RtlLengthRequiredSid(AuthCount));
+ if (NULL == NewSid) {
+ return NULL;
+ }
+
+ Status = RtlInitializeSid(NewSid, RtlIdentifierAuthoritySid(DomainSid),
+ AuthCount);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: 0x%x\n", Status));
+ }
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Copy the Domain Sid.
+ //
+
+ for (i = 0; i < AuthCount - 1; ++i) {
+ *RtlSubAuthoritySid(NewSid, i) = *RtlSubAuthoritySid(DomainSid,
+ i);
+ }
+
+ //
+ // Append the Relative Id.
+ //
+
+ *RtlSubAuthoritySid(NewSid, AuthCount - 1) = RelativeId;
+
+ return NewSid;
+}
+
+//
+// ConvertPathToPsx -- Converts an ANSI_STRING representation of
+// a path to a posix format path. The ANSI_STRING's buffer is assumed
+// to point to a section of the ClientView memory, and the MaximumLength
+// member of the ANSI_STRING is assumed to be set to the maximum length
+// of the final path string. This function will modify the Buffer and
+// Length members of the input ANSI_STRING. This function will return
+// false if a working buffer cannot be allocated in the PsxHeap heap.
+//
+static BOOLEAN
+ConvertPathToPsx (
+ ANSI_STRING *A
+ )
+{
+ PCHAR TmpBuff;
+ PCHAR InRover;
+ PCHAR OutRover;
+
+ TmpBuff = RtlAllocateHeap(PsxHeap, 0, A->Length*2);
+ if (NULL == TmpBuff) {
+ return(FALSE);
+ }
+ if (*(A->Buffer) == '\0') {
+ strcpy(TmpBuff, "//C/" );
+ } else {
+ for (InRover = A->Buffer, OutRover = TmpBuff;
+ *InRover != '\0';
+ ++InRover) {
+ if (';' == *InRover) {
+ // semis become colons
+ *OutRover++ = ':';
+ } else if ('\\' == *InRover) {
+ // back-slashes become forward-slashes
+ *OutRover++ = '/';
+ } else if (':' == *(InRover + 1)) {
+ // "X:" becomes "//X" - drive letter must be uppercase
+ *OutRover++ = '/';
+ *OutRover++ = '/';
+ *OutRover++ = toupper(*InRover);
+ ++InRover; // skip the colon
+ } else {
+ *OutRover++ = *InRover;
+ }
+ }
+ *OutRover = '\0';
+ }
+ strcpy(A->Buffer, TmpBuff);
+ A->Length = strlen(A->Buffer);
+ RtlFreeHeap(PsxHeap, 0, (PVOID)TmpBuff);
+ return (TRUE);
+}
+
+PSID
+GetSpecialSid(
+ uid_t Uid
+ )
+{
+ PSID Sid;
+ NTSTATUS Status;
+ SID_IDENTIFIER_AUTHORITY Auth = SECURITY_NULL_SID_AUTHORITY;
+ UCHAR uc;
+ ULONG ul;
+
+ Sid = RtlAllocateHeap(PsxHeap, 0, RtlLengthRequiredSid(1));
+ if (NULL == Sid)
+ return NULL;
+
+ Status = RtlInitializeSid(Sid, &Auth, 1);
+ ASSERT(NT_SUCCESS(Status));
+
+ uc = (UCHAR)((Uid & 0xFFF) >> 8);
+ RtlIdentifierAuthoritySid(Sid)->Value[5] = uc;
+
+ ul = Uid & 0xF;
+ *RtlSubAuthoritySid(Sid, 0) = ul;
+
+ return Sid;
+}
diff --git a/private/posix/psxss/timer.c b/private/posix/psxss/timer.c
new file mode 100644
index 000000000..afb75c456
--- /dev/null
+++ b/private/posix/psxss/timer.c
@@ -0,0 +1,322 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ timer.c
+
+Abstract:
+
+ This module implements POSIX timer related services.
+
+Author:
+
+ Mark Lucovsky (markl) 08-Aug-1989
+
+Revision History:
+
+--*/
+
+#include "psxsrv.h"
+
+typedef struct _ALARM_WORK_ITEM {
+ LIST_ENTRY Links;
+ PPSX_PROCESS Process;
+ LARGE_INTEGER Time;
+} ALARM_WORK_ITEM, *PALARM_WORK_ITEM;
+
+static LIST_ENTRY AlarmWorkList;
+static RTL_CRITICAL_SECTION AlarmWorkListMutex;
+HANDLE AlarmThreadHandle;
+
+HANDLE AlarmInitEvent;
+
+
+VOID
+AlarmApcRoutine(
+ IN PVOID TimerContext,
+ IN ULONG TimerLowValue,
+ IN LONG TimerHighValue
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called when a process alarm timer expires. Its purpose
+ is to send a SIGALRM signal to the appropriate process.
+
+Arguments:
+
+ TimerContext - Specifies the process that is to be signaled.
+
+ TimerLowValue - Ignored.
+
+ TimerHighValue - Ignored.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PPSX_PROCESS p;
+
+ p = (PPSX_PROCESS)TimerContext;
+
+ //
+ // Get process table lock to see if process still has an alarm timer. If
+ // it does, then signal the process. Otherwise, drop alarm on the floor.
+ //
+
+ AcquireProcessStructureLock();
+
+ if (p->AlarmTimer) {
+ PsxSignalProcess(p, SIGALRM);
+ }
+
+ ReleaseProcessStructureLock();
+
+}
+
+BOOLEAN
+PsxAlarm(
+ IN PPSX_PROCESS p,
+ IN PPSX_API_MSG m
+ )
+
+/*++
+
+Routine Description:
+
+ This function implements the alarm() API. The problem we're having
+ here is that in order for the APC to be processed, the thread (who
+ calls NtSetTimer) must be waiting in an alertable state. This is not
+ the case for the Api Request Threads.
+
+ So we have a thread dedicated to processing alarm requests. When he's
+ not doing that, he waits on his own thread handle. The Api Request
+ Thread puts a work request on a queue and wakes the alarm thread. He
+ processes the work request and then waits again.
+
+Arguments:
+
+ p - Supplies the address of the calling process
+
+ m - Supplies the address of the related message
+
+Return Value:
+
+ TRUE - Always succeeds and generates a reply
+
+--*/
+
+{
+ PPSX_ALARM_MSG args;
+ TIMER_BASIC_INFORMATION TimerInfo;
+ NTSTATUS st;
+ PALARM_WORK_ITEM pItem;
+ HANDLE Timer;
+
+ args = &m->u.Alarm;
+
+ args->PreviousSeconds.LowPart = 0;
+ args->PreviousSeconds.HighPart = 0;
+
+ if (args->CancelAlarm) {
+
+ // Cancel the timer.
+
+ AcquireProcessStructureLock();
+
+ Timer = p->AlarmTimer;
+ p->AlarmTimer = NULL;
+
+ //
+ // After this point no alarms will be delivered to the process.
+ //
+
+ ReleaseProcessStructureLock();
+
+ if (NULL != Timer) {
+
+ //
+ // Query timer to determine signaled state and time
+ // remaining. If timer is already signaled, then
+ // return 0; otherwise, return the reported remaining
+ // time
+ //
+
+ st = NtQueryTimer(Timer, TimerBasicInformation,
+ &TimerInfo, sizeof(TIMER_BASIC_INFORMATION),
+ NULL);
+ if (!NT_SUCCESS(st)) {
+ KdPrint(("PSXSS: QueryTimer: 0x%x\n", st));
+ m->Error = ENOMEM;
+ return TRUE;
+ }
+
+ if (FALSE == TimerInfo.TimerState) {
+
+ //
+ // Timer is still active
+ //
+
+ args->PreviousSeconds.LowPart =
+ TimerInfo.RemainingTime.LowPart;
+ args->PreviousSeconds.HighPart =
+ TimerInfo.RemainingTime.HighPart;
+
+ st = NtCancelTimer(Timer,
+ &TimerInfo.TimerState);
+ ASSERT(NT_SUCCESS(st));
+
+ ASSERT(FALSE == TimerInfo.TimerState);
+ }
+ st = NtClose(Timer);
+ ASSERT(NT_SUCCESS(st));
+
+ } else {
+ //
+ // The timer was already NULL, so we were cancelling
+ // a timer that had not been set.
+ //
+ }
+ return TRUE;
+ }
+
+ //
+ // Set a timer.
+ //
+
+ if (p->AlarmTimer) {
+
+ //
+ // Query timer to determine signaled state and time remaining
+ // If timer is already signaled, then return 0; otherwise,
+ // return the reported remaining time.
+ //
+
+ st = NtQueryTimer(p->AlarmTimer, TimerBasicInformation,
+ &TimerInfo, sizeof(TIMER_BASIC_INFORMATION), NULL);
+
+ ASSERT(NT_SUCCESS(st));
+
+ if (TimerInfo.TimerState == FALSE) {
+
+ //
+ // Timer is still active
+ //
+
+ args->PreviousSeconds.LowPart =
+ TimerInfo.RemainingTime.LowPart;
+ args->PreviousSeconds.HighPart =
+ TimerInfo.RemainingTime.HighPart;
+
+ st = NtCancelTimer(p->AlarmTimer,
+ &TimerInfo.TimerState);
+ ASSERT(NT_SUCCESS(st));
+ }
+ } else {
+
+ //
+ // Process does not have a timer, so create one for it.
+ // The timer will not be deallocated until the process exits.
+ //
+
+ st = NtCreateTimer(&p->AlarmTimer,
+ TIMER_ALL_ACCESS,
+ NULL,
+ NotificationTimer);
+
+ if (!NT_SUCCESS(st)) {
+ m->Error = ENOMEM;
+ return TRUE;
+ }
+ }
+
+ //
+ // Arrange for the alarm thread to set the timer.
+ //
+
+ st = NtResetEvent(AlarmInitEvent, NULL);
+ ASSERT(NT_SUCCESS(st));
+
+ pItem = (PVOID)RtlAllocateHeap(PsxHeap, 0, sizeof(*pItem));
+ if (NULL == pItem) {
+ m->Error = ENOMEM;
+ return TRUE;
+ }
+
+ pItem->Process = p;
+ pItem->Time = args->Seconds;
+
+ RtlEnterCriticalSection(&AlarmWorkListMutex);
+
+ InsertTailList(&AlarmWorkList, &pItem->Links);
+
+ RtlLeaveCriticalSection(&AlarmWorkListMutex);
+
+ //
+ // Wake up the Alarm Thread to process the work item.
+ //
+
+ st = NtAlertThread(AlarmThreadHandle);
+ ASSERT(NT_SUCCESS(st));
+
+ //
+ // Block until the work item has been processed. If we don't do
+ // this, there can be cases where an alarm is queried before the
+ // alarm thread has actually initialized the timer.
+ //
+
+ st = NtWaitForSingleObject(AlarmInitEvent, FALSE, NULL);
+
+ return TRUE;
+}
+
+VOID
+AlarmThreadRoutine(VOID)
+{
+ NTSTATUS Status;
+ PALARM_WORK_ITEM pItem;
+
+ RtlInitializeCriticalSection(&AlarmWorkListMutex);
+ InitializeListHead(&AlarmWorkList);
+
+ Status = NtCreateEvent(&AlarmInitEvent, EVENT_ALL_ACCESS,
+ NULL, NotificationEvent, TRUE);
+ ASSERT(NT_SUCCESS(Status));
+
+ for (;;) {
+ (void)NtWaitForSingleObject(NtCurrentThread(), TRUE, NULL);
+
+ RtlEnterCriticalSection(&AlarmWorkListMutex);
+
+ while (!IsListEmpty(&AlarmWorkList)) {
+
+ pItem = (PVOID)RemoveHeadList(&AlarmWorkList);
+
+ Status = NtSetTimer(pItem->Process->AlarmTimer,
+ &pItem->Time,
+ AlarmApcRoutine,
+ pItem->Process,
+ FALSE,
+ 0,
+ NULL);
+
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("PSXSS: AlarmThread: NtSetTime: 0x%x\n", Status));
+ }
+
+ RtlFreeHeap(PsxHeap, 0, pItem);
+ }
+
+ RtlLeaveCriticalSection(&AlarmWorkListMutex);
+
+ Status = NtSetEvent(AlarmInitEvent, NULL);
+ ASSERT(NT_SUCCESS(Status));
+ }
+}
diff --git a/private/posix/rtl/makefile b/private/posix/rtl/makefile
new file mode 100644
index 000000000..afc6030de
--- /dev/null
+++ b/private/posix/rtl/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT.
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/posix/rtl/sources b/private/posix/rtl/sources
new file mode 100644
index 000000000..efb266399
--- /dev/null
+++ b/private/posix/rtl/sources
@@ -0,0 +1,39 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=posix
+MINORCOMP=rtl
+
+TARGETNAME=psxrtl
+TARGETPATH=$(BASEDIR)\public\sdk\lib
+TARGETTYPE=LIBRARY
+
+INCLUDES=..\inc;$(BASEDIR)\public\sdk\inc\posix;$(BASEDIR)\public\sdk\inc\posix\sys
+
+SOURCES= startup.c \
+ stubs.c
+
+C_DEFINES=-D_POSIX_SOURCE=1
+UMTYPE=posix
+UMBASE=0xa00000
diff --git a/private/posix/rtl/startup.c b/private/posix/rtl/startup.c
new file mode 100644
index 000000000..a141d77af
--- /dev/null
+++ b/private/posix/rtl/startup.c
@@ -0,0 +1,68 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ startup.c
+
+Abstract:
+
+ This module contains the entry point code for the POSIX subsystem.
+
+Author:
+
+ Ellen Aycock-Wright (ellena) 04-Jan-1990
+
+Environment:
+
+ User Mode only
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <types.h>
+
+/*
+ * Define errno and environ here, since the DLL can only export functions.
+ */
+
+int errno;
+char **environ;
+
+void __PdxInitializeData(int *perrno, char ***penviron);
+void _CRTAPI1 mainCRTStartup(void);
+
+void
+_CRTAPI1
+__PosixProcessStartup(
+ void
+ )
+
+/*++
+
+Routine Description:
+
+ The __PosixProcessStartup function will receive control from the code
+ when a POSIX application process starts up.
+
+ The parameters to this function are as they are defined for the
+ entry point of a POSIX image file.
+
+Arguments:
+
+Return Value:
+
+ None, does not return
+
+--*/
+{
+
+ // Call POSIX Dll to "export" errno location.
+
+ __PdxInitializeData(&errno, &environ);
+
+ mainCRTStartup();
+}
diff --git a/private/posix/rtl/stubs.c b/private/posix/rtl/stubs.c
new file mode 100644
index 000000000..eb7e8abe2
--- /dev/null
+++ b/private/posix/rtl/stubs.c
@@ -0,0 +1,36 @@
+
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ stubs.c
+
+Abstract:
+
+ This module contains stubs for routines that posix crts are lacking.
+
+Author:
+
+ Ellen Aycock-Wright (ellena) 04-Jan-1990
+
+Environment:
+
+ User Mode only
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+
+void
+_chkstk()
+{
+}
+
+void
+_fptrap()
+{
+}
diff --git a/private/posix/status.psx b/private/posix/status.psx
new file mode 100644
index 000000000..b983affd7
--- /dev/null
+++ b/private/posix/status.psx
@@ -0,0 +1,306 @@
+The POSIX subsystem is modeled closely on the OS2 subsystem - I've re-arranged
+and renamed the files so that it will be easier to compare functionality.
+There is a problem with POSIX running in a window when a process forks
+that is probably due to the fact that keeping track of mulitple sessions
+is not done properly. Look at session.c in os2 and compare that code wiht
+posix.
+
+The arguments to exec are not getting through properly.
+
+It would be a good idea to check build.log after a complete build for warnings.
+I haven't done that in a while.
+
+The posix header files are not complete. They should be fleshed out (look
+at the xenix headers on you mail machine) so that the NIST tests will compile
+and so th languages guys know where conflicts may arise when they do the
+merge of the posix headers and c runtimes into their main source tree.
+
+On exit, smcomplete session has been returning c0000008 for a long time
+and I haven't taken time to track that down.
+
+See note 6 below re Console IO. I have no test (we need a simple shell)
+that does stdin as yet so that is not tested.
+
+Good luck!!
+
+
+POSIX STATUS
+
+ Nov 15, 1990 Feb 15, 1991
+
+Total functions: 97
+Done 44 60
+Optional (debatable) 12
+ --
+Unimplemented 41 25
+
+Process Primitives
+
+ fork /
+ execl /
+ execv /
+ execle /
+ execve /
+ execlp convert file to path using PATH env variable
+ execvp "
+
+ wait /
+ waitpid /
+ _exit /
+ kill /
+ sigemptyset / NOTE 1
+ sigfillset /
+ sigdelset /
+ sigismember /
+ sigaction /
+ sigprocmask /
+ sigpending
+ sigsuspend /
+ alarm /
+ pause /
+ sleep /
+
+Process Environment
+
+ getpid /
+ getppid /
+ getuid /
+ geteuid /
+ getgid /
+ getegid /
+ setuid
+ setgid
+ getgroups
+ getlogin
+ cuserid
+
+ getpgrp /
+ setsid /
+ setpgid /
+ uname /
+ time /
+ times /
+ getenv
+ ctermid
+ ttyname
+ isatty
+ sysconf /
+
+Files and Directories
+
+ opendir / NOTE 2 re file times updating
+ readdir /
+ rewinddir /
+ closedir /
+ chdir /
+ getcwd /
+ open /
+ creat /
+ umask /
+ link NOTE 3
+ mkdir / NOTE 4
+ mkfifo / NOTE 4
+ unlink NOTE 3
+ rmdir /
+ rename NOTE 3 - and how does this relate to C runtime version??
+
+ stat / Always fail? - see comment fdapi.c, NT security chapter
+ fstat / NOTES 2, 3, 4
+ access / Needs RtlMakePosixAcl from JimK
+ chmod / Needs RtlIntrepretPosix and NtSetSecurityObject (JimK)
+ chown / Some open issues - su priv, security routines work?
+ utime / Need NT to POSIX uid/gid translations
+ pathconf / Done for now - need NtFsControlFile for configurable
+ fpathconf /
+
+
+Input and Output Primitives
+
+ pipe /
+ dup /
+ dup2 /
+ close /
+ read /
+ write /
+ fcntl
+ lseek /
+
+Device and Class Specific Functions
+
+ cfgetispeed ( All optional ???)
+ cfgetospeed ( tonye may be source for tty support - he's doing)
+ cfsetispeed ( serial drivers)
+ cfsetospeed
+ tcgetattr
+ tcsetattr
+ tcsendbreak
+ tcdrain
+ tcflush
+ tcflow
+ tcgetpgrp
+ tcsetpgrp
+
+Language-Specific Services for the C Programming Language
+
+ setlocale
+ fileno /
+ fdopen
+ sigsetjmp
+ siglongjmp
+ tzset
+
+System Databases
+
+ getgrgid
+ getgrnam
+ getpwuid
+ getpwnam
+
+-----------------------------
+NOTE 1
+ Re: signals
+ In order to make NT exception handling available to PSX processes,
+ check for exceptions in apiloop using exception port and convert
+ to signals.
+
+NOTE 2
+ Re: file times updating.
+ Implementing this for all relevant file systems functions will
+ need to be postponed until NTFS. GaryKi has been given a list
+ of PSX requirements for times updating. For now POSIX uses the
+ times from NtQueryInformationFile for all but local pipes. Local
+ pipe times are updated in the subsystem.
+ See filereqs.psx in POSIX directory.
+
+NOTE 3
+ Re: link and unlink and rename -
+ Implementing this will need to be postponed until NTFS. The Pinball
+ FS does not implement links right now since OS2 doesn't use them.
+ The POSIX file system will be NTFS. GaryKi and TomM have been
+ contacted about this requirement. Check changes to 1.003a re
+ symbolic links.
+
+NOTE 4
+ Re: security
+ When security code is complete - these functions need to check
+ the ACL and/or UID GID SID info appropriately. See Security section
+ of workbook - chapter 14. Mike Massa (mikemas) is working on
+ security as he ports streams over to NT and POSIX. He was planning
+ to write conversion functions (cracking ACLs to get POSIX rwx access)
+ that should be used by the subsystem also.
+
+TODO
+
+1. Details are missing in the pre-ellen functions (such as setting uid,
+ file times etc in file functions). Go over all functions carefully to
+ see that they are FULLY functional re: spec.
+
+2. Operations on local pipes need to update the times in the ionode.
+ Pipe() initializes these. Open, read and write (other??) need to
+ update manually.
+
+3. When security is in place re: ACLs on files - all functions manipulating
+ and/or accessing file modes (access, open, fstat, chmod) must translate
+ between POSIX file permissions and NT ACLs and update appropriately.
+
+4. Many routines in fdapi have to open the file (NtOpen) to get a handle
+ for other routines. Check NtOpens actions on the file times of the file
+ vs what's supposed to happen to those times via the posix routine.
+
+5. Check into all routines that say they must be fixed to work with
+ view memory (grep for view).
+
+6. (This may negate some comments above.) ALL I/O except OPEN, PIPES, and FILE
+ LOCKING needs to be moved into the client and out of the server. read,
+ write, stat, etc. NOTE: FileRead and FileWrite functions in psxfile.c use
+ NtReadVirtualMemory and NtWriteVirtualMemory to transfer the data
+ to be read from or written to the file between the user buffer and the
+ server buffer. This is a STOPGAP ONLY measure required for POSIX
+ to work now that NTWriteFile no longer accepts a parameter specifying that
+ the write should be done to an alternate process' data. When this code is
+ moved out into the client and done strictly in the client's space, this
+ code is obviously unnecessary.
+
+ Currently, Console IO is caught at the client level (see IS_CONSOLE_IO
+ in dllio.c) and vectored off to the posix.exe to do the proper windows
+ crt read and write.. This should be done as os2 does it (os2 has all io
+ on the client side) using a vector of functions and not special-casing.
+ (See IoVectorArray in os2\client\dllhandl.c).
+
+7. There are lots of places in the client code where the user parameters
+ (ptrs to) are taken at face value and not checked. These should be probed
+ for read and write access before using. Esp uname times time wait waitpid etc
+ in pdxproc.c).
+
+8. The POSIX C Runtimes (crts) are incomplete and patched together and
+ not merged into the regular source tree. Languages (jeffrob) are
+ supposed to do this 'any time now'. Until this work is done (which
+ includes supplying header files that do not conflict with posix header
+ files), building apps and tests is iffy. NIST testing is virtually
+ impossible. Currently I have a patched together studio.h in inc\posix
+ so that printf will work.
+ When this work is done, mainCRTstartup() may change names. It will have
+ to be changed in rtl/startup.c to match what is in the runtime.
+ CONTACTS: jeffrob, gregf (jeff's boss) Get LOUP to follow up.
+
+9. MIPS builds with only one warning - due to that fact that the MIPS
+ version of the CONTEXT_TO_PROGRAM_COUNTER macro in ntmips.h is
+ wrong (should be cast to a PVOID). Update - I haven't built for MIPS
+ since March 1991.
+
+TESTING issues
+
+STAT - test 'real' code when NtQuerySecurityObject is fixed.
+
+NIST tests - SteveSc has loaded them onto a system and will try to
+build with the posix runtimes that mukund did last summer. THis may
+be a frustrating experience until we have 'real'crts and headers from
+languages.
+*************************************************************************
+CRT INFO - this is mail from the student who fixed up a POSIX CRT for me.
+This work needs to be merged into the languages CRT tree and built
+regularly.
+
+Here's a list of the POSIX related stuff that has to be done to assimilate the stuff back into the tree:
+
+Heap, String, Convert, Misc: The code is the same as WIN32.
+
+Stdio : Changes littered along the souce code. Could probably be put in the source tree directly.
+
+Time : An issue that will be resolved. The POSIX version is based on the C7 code, and uses floating point.
+
+Startup: Code based on WIN32 version. Only stdenvp.c and crt0msg.c has any new code.
+
+Setjmp and Lngjmp: to be done.
+
+Include files: Most of them could be put in the tree directly. There are a few POSIX specific include files
+(dirent.h, grp.h, pwd.h, sys\wait.h, sys\utsname.h, sys\termios.h, sys\times.h, unistd.h) that have to be added. Additional macros are defined in the POSIX version of stat.h and the POSIX version of types.h defines some additonal types.
+
+The POSIX source files can be found on \\crt1\vangogh in a
+directory called posix. The posix-specific include files can
+be found in \posix\psxinc.
+
+I had a really good time working here this summer and I want to thank all of you for making it such a good experience.
+ Mukund
+
+The server is:
+
+ \\vangogh\rdcrt1 password: QIEPCKE
+
+The tree is: \posix.
+
+-Gregf
+
+*************************************************************************
+
+RtlUnixPathToNtPath()
+
+Consult with SteveWo to sort out what should be done here.
+We need to be able to designate the posix file system(s) (ntfs only due
+to links needing to be supported) and how to translate pathname
+(eg \posix) to filesystem (eg what used to be \\harddiskX\partitionY\posix)
+for NT.
+
+Call RtlPosixPathToNtPath in client wherever a path argument is passed in.
+
+There is currently a RtlDosPathToNtPath() function to use as a guide.
diff --git a/private/posix/tests.psx b/private/posix/tests.psx
new file mode 100644
index 000000000..6d830ac0e
--- /dev/null
+++ b/private/posix/tests.psx
@@ -0,0 +1,67 @@
+Our internal (minimal) tests are the tst*.c files in the posix\client directory.
+Currently, the default root for the POSIX subsystem is d:\psx.
+
+tstdir.c: 'tstdir /psx/test'
+
+ - requires a current version of tsthello.exe in d:\psx
+ - d:\psx\test directory must exist with 2 levels of subdirectories that
+ contain files.
+ - d:\psx\test\tstdirs directory must exist
+
+tstfile.c: ' tstfile tstf.one tstf.two'
+
+ - requires 3 files in d:\psx: tstf.one, tstf.two, out.dat. Contents
+ unimportant.
+
+ **** Currently fails when trying to recreate a file that already
+ exists because the wrong error code is returned from PbOpenExistingFcb
+ Garyki will fix this.
+
+tstfork.c : 'tstfork'
+
+ - requires tsthello.exe in d:\psx
+
+ **** Currently fails with Exception in ex0(). Used to work and no
+ changes make since that would effect this test.
+
+tsthello.c: 'tsthello string'
+
+ - just proves that you can get in and out of subsystem
+
+tstjc.c: 'tstjc'
+
+tstloop.c: 'tstloop'
+
+tstmd.c: 'tstmd string'
+
+ - requires that whatever string you give it for an argument cannot
+ exist as a directory in \psx
+
+tstmisc: 'tstmisc'
+
+ - requires the file d:\psx\conffile (contents unimportant)
+
+ - tests sysconf(), pathconf(), fpathconf(), uname(), time(), times()
+
+tstnpipe.c: 'tstnpipe named.pip'
+
+ - requires d:\psx\named.pip
+
+ **** Currently fails with erroneous error codes.
+
+tstrmdir.c: 'tstrmdir dirname'
+
+ - requires the following sub directories in d:\psx\test:
+ rmtst1 containing one file "ab"
+ rmtst2 containing one file ".a" (??)
+ rmtst3 containing one file "a."
+ rmtst4 containing one file "abcde"
+
+ - /psx/test must not have an existing subdirectory with the same name
+ as the dirname argument.
+
+tstsid.c: 'tstsid'
+
+tstsig.c: 'tstsig'
+
+tstumask.c: ' tstumask'