diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/posix | |
download | NT4.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')
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 Binary files differnew file mode 100644 index 000000000..5cdc78ba7 --- /dev/null +++ b/private/posix/pcd.doc 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; ®dummy = 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 = ®dummy; + 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 == ®dummy) { + 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 != ®dummy) + *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 == ®dummy) { + 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 == ®dummy) + 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 == ®dummy || 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 == ®dummy) + 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' |