Initial copy of jnr-posix

Change-Id: Iff648b0fd3faa67dbec0549bd5f88056abd9ce8a
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..4adfdaa
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,1076 @@
+jnr-posix is released under a tri EPL/GPL/LGPL license. You can use it,
+redistribute it and/or modify it under the terms of the:
+
+  Eclipse Public License version 2.0
+    OR
+  GNU General Public License version 2
+    OR
+  GNU Lesser General Public License version 2.1
+
+The complete text of the Eclipse Public License is as follows:
+
+  Eclipse Public License - v 2.0
+
+      THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
+      PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION
+      OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+  1. DEFINITIONS
+
+  "Contribution" means:
+
+    a) in the case of the initial Contributor, the initial content
+       Distributed under this Agreement, and
+
+    b) in the case of each subsequent Contributor:
+       i) changes to the Program, and
+       ii) additions to the Program;
+    where such changes and/or additions to the Program originate from
+    and are Distributed by that particular Contributor. A Contribution
+    "originates" from a Contributor if it was added to the Program by
+    such Contributor itself or anyone acting on such Contributor's behalf.
+    Contributions do not include changes or additions to the Program that
+    are not Modified Works.
+
+  "Contributor" means any person or entity that Distributes the Program.
+
+  "Licensed Patents" mean patent claims licensable by a Contributor which
+  are necessarily infringed by the use or sale of its Contribution alone
+  or when combined with the Program.
+
+  "Program" means the Contributions Distributed in accordance with this
+  Agreement.
+
+  "Recipient" means anyone who receives the Program under this Agreement
+  or any Secondary License (as applicable), including Contributors.
+
+  "Derivative Works" shall mean any work, whether in Source Code or other
+  form, that is based on (or derived from) the Program and for which the
+  editorial revisions, annotations, elaborations, or other modifications
+  represent, as a whole, an original work of authorship.
+
+  "Modified Works" shall mean any work in Source Code or other form that
+  results from an addition to, deletion from, or modification of the
+  contents of the Program, including, for purposes of clarity any new file
+  in Source Code form that contains any contents of the Program. Modified
+  Works shall not include works that contain only declarations,
+  interfaces, types, classes, structures, or files of the Program solely
+  in each case in order to link to, bind by name, or subclass the Program
+  or Modified Works thereof.
+
+  "Distribute" means the acts of a) distributing or b) making available
+  in any manner that enables the transfer of a copy.
+
+  "Source Code" means the form of a Program preferred for making
+  modifications, including but not limited to software source code,
+  documentation source, and configuration files.
+
+  "Secondary License" means either the GNU General Public License,
+  Version 2.0, or any later versions of that license, including any
+  exceptions or additional permissions as identified by the initial
+  Contributor.
+
+  2. GRANT OF RIGHTS
+
+    a) Subject to the terms of this Agreement, each Contributor hereby
+    grants Recipient a non-exclusive, worldwide, royalty-free copyright
+    license to reproduce, prepare Derivative Works of, publicly display,
+    publicly perform, Distribute and sublicense the Contribution of such
+    Contributor, if any, and such Derivative Works.
+
+    b) Subject to the terms of this Agreement, each Contributor hereby
+    grants Recipient a non-exclusive, worldwide, royalty-free patent
+    license under Licensed Patents to make, use, sell, offer to sell,
+    import and otherwise transfer the Contribution of such Contributor,
+    if any, in Source Code or other form. This patent license shall
+    apply to the combination of the Contribution and the Program if, at
+    the time the Contribution is added by the Contributor, such addition
+    of the Contribution causes such combination to be covered by the
+    Licensed Patents. The patent license shall not apply to any other
+    combinations which include the Contribution. No hardware per se is
+    licensed hereunder.
+
+    c) Recipient understands that although each Contributor grants the
+    licenses to its Contributions set forth herein, no assurances are
+    provided by any Contributor that the Program does not infringe the
+    patent or other intellectual property rights of any other entity.
+    Each Contributor disclaims any liability to Recipient for claims
+    brought by any other entity based on infringement of intellectual
+    property rights or otherwise. As a condition to exercising the
+    rights and licenses granted hereunder, each Recipient hereby
+    assumes sole responsibility to secure any other intellectual
+    property rights needed, if any. For example, if a third party
+    patent license is required to allow Recipient to Distribute the
+    Program, it is Recipient's responsibility to acquire that license
+    before distributing the Program.
+
+    d) Each Contributor represents that to its knowledge it has
+    sufficient copyright rights in its Contribution, if any, to grant
+    the copyright license set forth in this Agreement.
+
+    e) Notwithstanding the terms of any Secondary License, no
+    Contributor makes additional grants to any Recipient (other than
+    those set forth in this Agreement) as a result of such Recipient's
+    receipt of the Program under the terms of a Secondary License
+    (if permitted under the terms of Section 3).
+
+  3. REQUIREMENTS
+
+  3.1 If a Contributor Distributes the Program in any form, then:
+
+    a) the Program must also be made available as Source Code, in
+    accordance with section 3.2, and the Contributor must accompany
+    the Program with a statement that the Source Code for the Program
+    is available under this Agreement, and informs Recipients how to
+    obtain it in a reasonable manner on or through a medium customarily
+    used for software exchange; and
+
+    b) the Contributor may Distribute the Program under a license
+    different than this Agreement, provided that such license:
+       i) effectively disclaims on behalf of all other Contributors all
+       warranties and conditions, express and implied, including
+       warranties or conditions of title and non-infringement, and
+       implied warranties or conditions of merchantability and fitness
+       for a particular purpose;
+
+       ii) effectively excludes on behalf of all other Contributors all
+       liability for damages, including direct, indirect, special,
+       incidental and consequential damages, such as lost profits;
+
+       iii) does not attempt to limit or alter the recipients' rights
+       in the Source Code under section 3.2; and
+
+       iv) requires any subsequent distribution of the Program by any
+       party to be under a license that satisfies the requirements
+       of this section 3.
+
+  3.2 When the Program is Distributed as Source Code:
+
+    a) it must be made available under this Agreement, or if the
+    Program (i) is combined with other material in a separate file or
+    files made available under a Secondary License, and (ii) the initial
+    Contributor attached to the Source Code the notice described in
+    Exhibit A of this Agreement, then the Program may be made available
+    under the terms of such Secondary Licenses, and
+
+    b) a copy of this Agreement must be included with each copy of
+    the Program.
+
+  3.3 Contributors may not remove or alter any copyright, patent,
+  trademark, attribution notices, disclaimers of warranty, or limitations
+  of liability ("notices") contained within the Program from any copy of
+  the Program which they Distribute, provided that Contributors may add
+  their own appropriate notices.
+
+  4. COMMERCIAL DISTRIBUTION
+
+  Commercial distributors of software may accept certain responsibilities
+  with respect to end users, business partners and the like. While this
+  license is intended to facilitate the commercial use of the Program,
+  the Contributor who includes the Program in a commercial product
+  offering should do so in a manner which does not create potential
+  liability for other Contributors. Therefore, if a Contributor includes
+  the Program in a commercial product offering, such Contributor
+  ("Commercial Contributor") hereby agrees to defend and indemnify every
+  other Contributor ("Indemnified Contributor") against any losses,
+  damages and costs (collectively "Losses") arising from claims, lawsuits
+  and other legal actions brought by a third party against the Indemnified
+  Contributor to the extent caused by the acts or omissions of such
+  Commercial Contributor in connection with its distribution of the Program
+  in a commercial product offering. The obligations in this section do not
+  apply to any claims or Losses relating to any actual or alleged
+  intellectual property infringement. In order to qualify, an Indemnified
+  Contributor must: a) promptly notify the Commercial Contributor in
+  writing of such claim, and b) allow the Commercial Contributor to control,
+  and cooperate with the Commercial Contributor in, the defense and any
+  related settlement negotiations. The Indemnified Contributor may
+  participate in any such claim at its own expense.
+
+  For example, a Contributor might include the Program in a commercial
+  product offering, Product X. That Contributor is then a Commercial
+  Contributor. If that Commercial Contributor then makes performance
+  claims, or offers warranties related to Product X, those performance
+  claims and warranties are such Commercial Contributor's responsibility
+  alone. Under this section, the Commercial Contributor would have to
+  defend claims against the other Contributors related to those performance
+  claims and warranties, and if a court requires any other Contributor to
+  pay any damages as a result, the Commercial Contributor must pay
+  those damages.
+
+  5. NO WARRANTY
+
+  EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
+  PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS"
+  BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
+  IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF
+  TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
+  PURPOSE. Each Recipient is solely responsible for determining the
+  appropriateness of using and distributing the Program and assumes all
+  risks associated with its exercise of rights under this Agreement,
+  including but not limited to the risks and costs of program errors,
+  compliance with applicable laws, damage to or loss of data, programs
+  or equipment, and unavailability or interruption of operations.
+
+  6. DISCLAIMER OF LIABILITY
+
+  EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
+  PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS
+  SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
+  PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+  ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
+  EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
+  POSSIBILITY OF SUCH DAMAGES.
+
+  7. GENERAL
+
+  If any provision of this Agreement is invalid or unenforceable under
+  applicable law, it shall not affect the validity or enforceability of
+  the remainder of the terms of this Agreement, and without further
+  action by the parties hereto, such provision shall be reformed to the
+  minimum extent necessary to make such provision valid and enforceable.
+
+  If Recipient institutes patent litigation against any entity
+  (including a cross-claim or counterclaim in a lawsuit) alleging that the
+  Program itself (excluding combinations of the Program with other software
+  or hardware) infringes such Recipient's patent(s), then such Recipient's
+  rights granted under Section 2(b) shall terminate as of the date such
+  litigation is filed.
+
+  All Recipient's rights under this Agreement shall terminate if it
+  fails to comply with any of the material terms or conditions of this
+  Agreement and does not cure such failure in a reasonable period of
+  time after becoming aware of such noncompliance. If all Recipient's
+  rights under this Agreement terminate, Recipient agrees to cease use
+  and distribution of the Program as soon as reasonably practicable.
+  However, Recipient's obligations under this Agreement and any licenses
+  granted by Recipient relating to the Program shall continue and survive.
+
+  Everyone is permitted to copy and distribute copies of this Agreement,
+  but in order to avoid inconsistency the Agreement is copyrighted and
+  may only be modified in the following manner. The Agreement Steward
+  reserves the right to publish new versions (including revisions) of
+  this Agreement from time to time. No one other than the Agreement
+  Steward has the right to modify this Agreement. The Eclipse Foundation
+  is the initial Agreement Steward. The Eclipse Foundation may assign the
+  responsibility to serve as the Agreement Steward to a suitable separate
+  entity. Each new version of the Agreement will be given a distinguishing
+  version number. The Program (including Contributions) may always be
+  Distributed subject to the version of the Agreement under which it was
+  received. In addition, after a new version of the Agreement is published,
+  Contributor may elect to Distribute the Program (including its
+  Contributions) under the new version.
+
+  Except as expressly stated in Sections 2(a) and 2(b) above, Recipient
+  receives no rights or licenses to the intellectual property of any
+  Contributor under this Agreement, whether expressly, by implication,
+  estoppel or otherwise. All rights in the Program not expressly granted
+  under this Agreement are reserved. Nothing in this Agreement is intended
+  to be enforceable by any entity that is not a Contributor or Recipient.
+  No third-party beneficiary rights are created under this Agreement.
+
+  Exhibit A - Form of Secondary Licenses Notice
+
+  "This Source Code may also be made available under the following
+  Secondary Licenses when the conditions for such availability set forth
+  in the Eclipse Public License, v. 2.0 are satisfied: {name license(s),
+  version(s), and exceptions or additional permissions here}."
+
+    Simply including a copy of this Agreement, including this Exhibit A
+    is not sufficient to license the Source Code under Secondary Licenses.
+
+    If it is not possible or desirable to put the notice in a particular
+    file, then You may include the notice in a location (such as a LICENSE
+    file in a relevant directory) where a recipient would be likely to
+    look for such a notice.
+
+    You may add additional accurate notices of copyright ownership.
+
+The complete text of the GNU General Public License v2 is as follows:
+
+          GNU GENERAL PUBLIC LICENSE
+             Version 2, June 1991
+
+   Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                         59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+   Everyone is permitted to copy and distribute verbatim copies
+   of this license document, but changing it is not allowed.
+
+            Preamble
+
+    The licenses for most software are designed to take away your
+  freedom to share and change it.  By contrast, the GNU General Public
+  License is intended to guarantee your freedom to share and change free
+  software--to make sure the software is free for all its users.  This
+  General Public License applies to most of the Free Software
+  Foundation's software and to any other program whose authors commit to
+  using it.  (Some other Free Software Foundation software is covered by
+  the GNU Library General Public License instead.)  You can apply it to
+  your programs, too.
+
+    When we speak of free software, we are referring to freedom, not
+  price.  Our General Public Licenses are designed to make sure that you
+  have the freedom to distribute copies of free software (and charge for
+  this service if you wish), that you receive source code or can get it
+  if you want it, that you can change the software or use pieces of it
+  in new free programs; and that you know you can do these things.
+
+    To protect your rights, we need to make restrictions that forbid
+  anyone to deny you these rights or to ask you to surrender the rights.
+  These restrictions translate to certain responsibilities for you if you
+  distribute copies of the software, or if you modify it.
+
+    For example, if you distribute copies of such a program, whether
+  gratis or for a fee, you must give the recipients all the rights that
+  you have.  You must make sure that they, too, receive or can get the
+  source code.  And you must show them these terms so they know their
+  rights.
+
+    We protect your rights with two steps: (1) copyright the software, and
+  (2) offer you this license which gives you legal permission to copy,
+  distribute and/or modify the software.
+
+    Also, for each author's protection and ours, we want to make certain
+  that everyone understands that there is no warranty for this free
+  software.  If the software is modified by someone else and passed on, we
+  want its recipients to know that what they have is not the original, so
+  that any problems introduced by others will not reflect on the original
+  authors' reputations.
+
+    Finally, any free program is threatened constantly by software
+  patents.  We wish to avoid the danger that redistributors of a free
+  program will individually obtain patent licenses, in effect making the
+  program proprietary.  To prevent this, we have made it clear that any
+  patent must be licensed for everyone's free use or not licensed at all.
+
+    The precise terms and conditions for copying, distribution and
+  modification follow.
+
+          GNU GENERAL PUBLIC LICENSE
+     TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+    0. This License applies to any program or other work which contains
+  a notice placed by the copyright holder saying it may be distributed
+  under the terms of this General Public License.  The "Program", below,
+  refers to any such program or work, and a "work based on the Program"
+  means either the Program or any derivative work under copyright law:
+  that is to say, a work containing the Program or a portion of it,
+  either verbatim or with modifications and/or translated into another
+  language.  (Hereinafter, translation is included without limitation in
+  the term "modification".)  Each licensee is addressed as "you".
+
+  Activities other than copying, distribution and modification are not
+  covered by this License; they are outside its scope.  The act of
+  running the Program is not restricted, and the output from the Program
+  is covered only if its contents constitute a work based on the
+  Program (independent of having been made by running the Program).
+  Whether that is true depends on what the Program does.
+
+    1. You may copy and distribute verbatim copies of the Program's
+  source code as you receive it, in any medium, provided that you
+  conspicuously and appropriately publish on each copy an appropriate
+  copyright notice and disclaimer of warranty; keep intact all the
+  notices that refer to this License and to the absence of any warranty;
+  and give any other recipients of the Program a copy of this License
+  along with the Program.
+
+  You may charge a fee for the physical act of transferring a copy, and
+  you may at your option offer warranty protection in exchange for a fee.
+
+    2. You may modify your copy or copies of the Program or any portion
+  of it, thus forming a work based on the Program, and copy and
+  distribute such modifications or work under the terms of Section 1
+  above, provided that you also meet all of these conditions:
+
+      a) You must cause the modified files to carry prominent notices
+      stating that you changed the files and the date of any change.
+
+      b) You must cause any work that you distribute or publish, that in
+      whole or in part contains or is derived from the Program or any
+      part thereof, to be licensed as a whole at no charge to all third
+      parties under the terms of this License.
+
+      c) If the modified program normally reads commands interactively
+      when run, you must cause it, when started running for such
+      interactive use in the most ordinary way, to print or display an
+      announcement including an appropriate copyright notice and a
+      notice that there is no warranty (or else, saying that you provide
+      a warranty) and that users may redistribute the program under
+      these conditions, and telling the user how to view a copy of this
+      License.  (Exception: if the Program itself is interactive but
+      does not normally print such an announcement, your work based on
+      the Program is not required to print an announcement.)
+
+  These requirements apply to the modified work as a whole.  If
+  identifiable sections of that work are not derived from the Program,
+  and can be reasonably considered independent and separate works in
+  themselves, then this License, and its terms, do not apply to those
+  sections when you distribute them as separate works.  But when you
+  distribute the same sections as part of a whole which is a work based
+  on the Program, the distribution of the whole must be on the terms of
+  this License, whose permissions for other licensees extend to the
+  entire whole, and thus to each and every part regardless of who wrote it.
+
+  Thus, it is not the intent of this section to claim rights or contest
+  your rights to work written entirely by you; rather, the intent is to
+  exercise the right to control the distribution of derivative or
+  collective works based on the Program.
+
+  In addition, mere aggregation of another work not based on the Program
+  with the Program (or with a work based on the Program) on a volume of
+  a storage or distribution medium does not bring the other work under
+  the scope of this License.
+
+    3. You may copy and distribute the Program (or a work based on it,
+  under Section 2) in object code or executable form under the terms of
+  Sections 1 and 2 above provided that you also do one of the following:
+
+      a) Accompany it with the complete corresponding machine-readable
+      source code, which must be distributed under the terms of Sections
+      1 and 2 above on a medium customarily used for software interchange; or,
+
+      b) Accompany it with a written offer, valid for at least three
+      years, to give any third party, for a charge no more than your
+      cost of physically performing source distribution, a complete
+      machine-readable copy of the corresponding source code, to be
+      distributed under the terms of Sections 1 and 2 above on a medium
+      customarily used for software interchange; or,
+
+      c) Accompany it with the information you received as to the offer
+      to distribute corresponding source code.  (This alternative is
+      allowed only for noncommercial distribution and only if you
+      received the program in object code or executable form with such
+      an offer, in accord with Subsection b above.)
+
+  The source code for a work means the preferred form of the work for
+  making modifications to it.  For an executable work, complete source
+  code means all the source code for all modules it contains, plus any
+  associated interface definition files, plus the scripts used to
+  control compilation and installation of the executable.  However, as a
+  special exception, the source code distributed need not include
+  anything that is normally distributed (in either source or binary
+  form) with the major components (compiler, kernel, and so on) of the
+  operating system on which the executable runs, unless that component
+  itself accompanies the executable.
+
+  If distribution of executable or object code is made by offering
+  access to copy from a designated place, then offering equivalent
+  access to copy the source code from the same place counts as
+  distribution of the source code, even though third parties are not
+  compelled to copy the source along with the object code.
+
+    4. You may not copy, modify, sublicense, or distribute the Program
+  except as expressly provided under this License.  Any attempt
+  otherwise to copy, modify, sublicense or distribute the Program is
+  void, and will automatically terminate your rights under this License.
+  However, parties who have received copies, or rights, from you under
+  this License will not have their licenses terminated so long as such
+  parties remain in full compliance.
+
+    5. You are not required to accept this License, since you have not
+  signed it.  However, nothing else grants you permission to modify or
+  distribute the Program or its derivative works.  These actions are
+  prohibited by law if you do not accept this License.  Therefore, by
+  modifying or distributing the Program (or any work based on the
+  Program), you indicate your acceptance of this License to do so, and
+  all its terms and conditions for copying, distributing or modifying
+  the Program or works based on it.
+
+    6. Each time you redistribute the Program (or any work based on the
+  Program), the recipient automatically receives a license from the
+  original licensor to copy, distribute or modify the Program subject to
+  these terms and conditions.  You may not impose any further
+  restrictions on the recipients' exercise of the rights granted herein.
+  You are not responsible for enforcing compliance by third parties to
+  this License.
+
+    7. If, as a consequence of a court judgment or allegation of patent
+  infringement or for any other reason (not limited to patent issues),
+  conditions are imposed on you (whether by court order, agreement or
+  otherwise) that contradict the conditions of this License, they do not
+  excuse you from the conditions of this License.  If you cannot
+  distribute so as to satisfy simultaneously your obligations under this
+  License and any other pertinent obligations, then as a consequence you
+  may not distribute the Program at all.  For example, if a patent
+  license would not permit royalty-free redistribution of the Program by
+  all those who receive copies directly or indirectly through you, then
+  the only way you could satisfy both it and this License would be to
+  refrain entirely from distribution of the Program.
+
+  If any portion of this section is held invalid or unenforceable under
+  any particular circumstance, the balance of the section is intended to
+  apply and the section as a whole is intended to apply in other
+  circumstances.
+
+  It is not the purpose of this section to induce you to infringe any
+  patents or other property right claims or to contest validity of any
+  such claims; this section has the sole purpose of protecting the
+  integrity of the free software distribution system, which is
+  implemented by public license practices.  Many people have made
+  generous contributions to the wide range of software distributed
+  through that system in reliance on consistent application of that
+  system; it is up to the author/donor to decide if he or she is willing
+  to distribute software through any other system and a licensee cannot
+  impose that choice.
+
+  This section is intended to make thoroughly clear what is believed to
+  be a consequence of the rest of this License.
+
+    8. If the distribution and/or use of the Program is restricted in
+  certain countries either by patents or by copyrighted interfaces, the
+  original copyright holder who places the Program under this License
+  may add an explicit geographical distribution limitation excluding
+  those countries, so that distribution is permitted only in or among
+  countries not thus excluded.  In such case, this License incorporates
+  the limitation as if written in the body of this License.
+
+    9. The Free Software Foundation may publish revised and/or new versions
+  of the General Public License from time to time.  Such new versions will
+  be similar in spirit to the present version, but may differ in detail to
+  address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the Program
+  specifies a version number of this License which applies to it and "any
+  later version", you have the option of following the terms and conditions
+  either of that version or of any later version published by the Free
+  Software Foundation.  If the Program does not specify a version number of
+  this License, you may choose any version ever published by the Free Software
+  Foundation.
+
+    10. If you wish to incorporate parts of the Program into other free
+  programs whose distribution conditions are different, write to the author
+  to ask for permission.  For software which is copyrighted by the Free
+  Software Foundation, write to the Free Software Foundation; we sometimes
+  make exceptions for this.  Our decision will be guided by the two goals
+  of preserving the free status of all derivatives of our free software and
+  of promoting the sharing and reuse of software generally.
+
+            NO WARRANTY
+
+    11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+  FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+  OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+  PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+  OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+  TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+  PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+  REPAIR OR CORRECTION.
+
+    12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+  WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+  REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+  INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+  OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+  TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+  YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+  PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+  POSSIBILITY OF SUCH DAMAGES.
+
+           END OF TERMS AND CONDITIONS
+
+The complete text of the GNU Lesser General Public License 2.1 is as follows:
+
+        GNU LESSER GENERAL PUBLIC LICENSE
+             Version 2.1, February 1999
+
+   Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+   Everyone is permitted to copy and distribute verbatim copies
+   of this license document, but changing it is not allowed.
+
+  [This is the first released version of the Lesser GPL.  It also counts
+   as the successor of the GNU Library Public License, version 2, hence
+   the version number 2.1.]
+
+            Preamble
+
+    The licenses for most software are designed to take away your
+  freedom to share and change it.  By contrast, the GNU General Public
+  Licenses are intended to guarantee your freedom to share and change
+  free software--to make sure the software is free for all its users.
+
+    This license, the Lesser General Public License, applies to some
+  specially designated software packages--typically libraries--of the
+  Free Software Foundation and other authors who decide to use it.  You
+  can use it too, but we suggest you first think carefully about whether
+  this license or the ordinary General Public License is the better
+  strategy to use in any particular case, based on the explanations below.
+
+    When we speak of free software, we are referring to freedom of use,
+  not price.  Our General Public Licenses are designed to make sure that
+  you have the freedom to distribute copies of free software (and charge
+  for this service if you wish); that you receive source code or can get
+  it if you want it; that you can change the software and use pieces of
+  it in new free programs; and that you are informed that you can do
+  these things.
+
+    To protect your rights, we need to make restrictions that forbid
+  distributors to deny you these rights or to ask you to surrender these
+  rights.  These restrictions translate to certain responsibilities for
+  you if you distribute copies of the library or if you modify it.
+
+    For example, if you distribute copies of the library, whether gratis
+  or for a fee, you must give the recipients all the rights that we gave
+  you.  You must make sure that they, too, receive or can get the source
+  code.  If you link other code with the library, you must provide
+  complete object files to the recipients, so that they can relink them
+  with the library after making changes to the library and recompiling
+  it.  And you must show them these terms so they know their rights.
+
+    We protect your rights with a two-step method: (1) we copyright the
+  library, and (2) we offer you this license, which gives you legal
+  permission to copy, distribute and/or modify the library.
+
+    To protect each distributor, we want to make it very clear that
+  there is no warranty for the free library.  Also, if the library is
+  modified by someone else and passed on, the recipients should know
+  that what they have is not the original version, so that the original
+  author's reputation will not be affected by problems that might be
+  introduced by others.
+
+    Finally, software patents pose a constant threat to the existence of
+  any free program.  We wish to make sure that a company cannot
+  effectively restrict the users of a free program by obtaining a
+  restrictive license from a patent holder.  Therefore, we insist that
+  any patent license obtained for a version of the library must be
+  consistent with the full freedom of use specified in this license.
+
+    Most GNU software, including some libraries, is covered by the
+  ordinary GNU General Public License.  This license, the GNU Lesser
+  General Public License, applies to certain designated libraries, and
+  is quite different from the ordinary General Public License.  We use
+  this license for certain libraries in order to permit linking those
+  libraries into non-free programs.
+
+    When a program is linked with a library, whether statically or using
+  a shared library, the combination of the two is legally speaking a
+  combined work, a derivative of the original library.  The ordinary
+  General Public License therefore permits such linking only if the
+  entire combination fits its criteria of freedom.  The Lesser General
+  Public License permits more lax criteria for linking other code with
+  the library.
+
+    We call this license the "Lesser" General Public License because it
+  does Less to protect the user's freedom than the ordinary General
+  Public License.  It also provides other free software developers Less
+  of an advantage over competing non-free programs.  These disadvantages
+  are the reason we use the ordinary General Public License for many
+  libraries.  However, the Lesser license provides advantages in certain
+  special circumstances.
+
+    For example, on rare occasions, there may be a special need to
+  encourage the widest possible use of a certain library, so that it becomes
+  a de-facto standard.  To achieve this, non-free programs must be
+  allowed to use the library.  A more frequent case is that a free
+  library does the same job as widely used non-free libraries.  In this
+  case, there is little to gain by limiting the free library to free
+  software only, so we use the Lesser General Public License.
+
+    In other cases, permission to use a particular library in non-free
+  programs enables a greater number of people to use a large body of
+  free software.  For example, permission to use the GNU C Library in
+  non-free programs enables many more people to use the whole GNU
+  operating system, as well as its variant, the GNU/Linux operating
+  system.
+
+    Although the Lesser General Public License is Less protective of the
+  users' freedom, it does ensure that the user of a program that is
+  linked with the Library has the freedom and the wherewithal to run
+  that program using a modified version of the Library.
+
+    The precise terms and conditions for copying, distribution and
+  modification follow.  Pay close attention to the difference between a
+  "work based on the library" and a "work that uses the library".  The
+  former contains code derived from the library, whereas the latter must
+  be combined with the library in order to run.
+
+        GNU LESSER GENERAL PUBLIC LICENSE
+     TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+    0. This License Agreement applies to any software library or other
+  program which contains a notice placed by the copyright holder or
+  other authorized party saying it may be distributed under the terms of
+  this Lesser General Public License (also called "this License").
+  Each licensee is addressed as "you".
+
+    A "library" means a collection of software functions and/or data
+  prepared so as to be conveniently linked with application programs
+  (which use some of those functions and data) to form executables.
+
+    The "Library", below, refers to any such software library or work
+  which has been distributed under these terms.  A "work based on the
+  Library" means either the Library or any derivative work under
+  copyright law: that is to say, a work containing the Library or a
+  portion of it, either verbatim or with modifications and/or translated
+  straightforwardly into another language.  (Hereinafter, translation is
+  included without limitation in the term "modification".)
+
+    "Source code" for a work means the preferred form of the work for
+  making modifications to it.  For a library, complete source code means
+  all the source code for all modules it contains, plus any associated
+  interface definition files, plus the scripts used to control compilation
+  and installation of the library.
+
+    Activities other than copying, distribution and modification are not
+  covered by this License; they are outside its scope.  The act of
+  running a program using the Library is not restricted, and output from
+  such a program is covered only if its contents constitute a work based
+  on the Library (independent of the use of the Library in a tool for
+  writing it).  Whether that is true depends on what the Library does
+  and what the program that uses the Library does.
+  
+    1. You may copy and distribute verbatim copies of the Library's
+  complete source code as you receive it, in any medium, provided that
+  you conspicuously and appropriately publish on each copy an
+  appropriate copyright notice and disclaimer of warranty; keep intact
+  all the notices that refer to this License and to the absence of any
+  warranty; and distribute a copy of this License along with the
+  Library.
+
+    You may charge a fee for the physical act of transferring a copy,
+  and you may at your option offer warranty protection in exchange for a
+  fee.
+
+    2. You may modify your copy or copies of the Library or any portion
+  of it, thus forming a work based on the Library, and copy and
+  distribute such modifications or work under the terms of Section 1
+  above, provided that you also meet all of these conditions:
+
+      a) The modified work must itself be a software library.
+
+      b) You must cause the files modified to carry prominent notices
+      stating that you changed the files and the date of any change.
+
+      c) You must cause the whole of the work to be licensed at no
+      charge to all third parties under the terms of this License.
+
+      d) If a facility in the modified Library refers to a function or a
+      table of data to be supplied by an application program that uses
+      the facility, other than as an argument passed when the facility
+      is invoked, then you must make a good faith effort to ensure that,
+      in the event an application does not supply such function or
+      table, the facility still operates, and performs whatever part of
+      its purpose remains meaningful.
+
+      (For example, a function in a library to compute square roots has
+      a purpose that is entirely well-defined independent of the
+      application.  Therefore, Subsection 2d requires that any
+      application-supplied function or table used by this function must
+      be optional: if the application does not supply it, the square
+      root function must still compute square roots.)
+
+  These requirements apply to the modified work as a whole.  If
+  identifiable sections of that work are not derived from the Library,
+  and can be reasonably considered independent and separate works in
+  themselves, then this License, and its terms, do not apply to those
+  sections when you distribute them as separate works.  But when you
+  distribute the same sections as part of a whole which is a work based
+  on the Library, the distribution of the whole must be on the terms of
+  this License, whose permissions for other licensees extend to the
+  entire whole, and thus to each and every part regardless of who wrote
+  it.
+
+  Thus, it is not the intent of this section to claim rights or contest
+  your rights to work written entirely by you; rather, the intent is to
+  exercise the right to control the distribution of derivative or
+  collective works based on the Library.
+
+  In addition, mere aggregation of another work not based on the Library
+  with the Library (or with a work based on the Library) on a volume of
+  a storage or distribution medium does not bring the other work under
+  the scope of this License.
+
+    3. You may opt to apply the terms of the ordinary GNU General Public
+  License instead of this License to a given copy of the Library.  To do
+  this, you must alter all the notices that refer to this License, so
+  that they refer to the ordinary GNU General Public License, version 2,
+  instead of to this License.  (If a newer version than version 2 of the
+  ordinary GNU General Public License has appeared, then you can specify
+  that version instead if you wish.)  Do not make any other change in
+  these notices.
+
+    Once this change is made in a given copy, it is irreversible for
+  that copy, so the ordinary GNU General Public License applies to all
+  subsequent copies and derivative works made from that copy.
+
+    This option is useful when you wish to copy part of the code of
+  the Library into a program that is not a library.
+
+    4. You may copy and distribute the Library (or a portion or
+  derivative of it, under Section 2) in object code or executable form
+  under the terms of Sections 1 and 2 above provided that you accompany
+  it with the complete corresponding machine-readable source code, which
+  must be distributed under the terms of Sections 1 and 2 above on a
+  medium customarily used for software interchange.
+
+    If distribution of object code is made by offering access to copy
+  from a designated place, then offering equivalent access to copy the
+  source code from the same place satisfies the requirement to
+  distribute the source code, even though third parties are not
+  compelled to copy the source along with the object code.
+
+    5. A program that contains no derivative of any portion of the
+  Library, but is designed to work with the Library by being compiled or
+  linked with it, is called a "work that uses the Library".  Such a
+  work, in isolation, is not a derivative work of the Library, and
+  therefore falls outside the scope of this License.
+
+    However, linking a "work that uses the Library" with the Library
+  creates an executable that is a derivative of the Library (because it
+  contains portions of the Library), rather than a "work that uses the
+  library".  The executable is therefore covered by this License.
+  Section 6 states terms for distribution of such executables.
+
+    When a "work that uses the Library" uses material from a header file
+  that is part of the Library, the object code for the work may be a
+  derivative work of the Library even though the source code is not.
+  Whether this is true is especially significant if the work can be
+  linked without the Library, or if the work is itself a library.  The
+  threshold for this to be true is not precisely defined by law.
+
+    If such an object file uses only numerical parameters, data
+  structure layouts and accessors, and small macros and small inline
+  functions (ten lines or less in length), then the use of the object
+  file is unrestricted, regardless of whether it is legally a derivative
+  work.  (Executables containing this object code plus portions of the
+  Library will still fall under Section 6.)
+
+    Otherwise, if the work is a derivative of the Library, you may
+  distribute the object code for the work under the terms of Section 6.
+  Any executables containing that work also fall under Section 6,
+  whether or not they are linked directly with the Library itself.
+
+    6. As an exception to the Sections above, you may also combine or
+  link a "work that uses the Library" with the Library to produce a
+  work containing portions of the Library, and distribute that work
+  under terms of your choice, provided that the terms permit
+  modification of the work for the customer's own use and reverse
+  engineering for debugging such modifications.
+
+    You must give prominent notice with each copy of the work that the
+  Library is used in it and that the Library and its use are covered by
+  this License.  You must supply a copy of this License.  If the work
+  during execution displays copyright notices, you must include the
+  copyright notice for the Library among them, as well as a reference
+  directing the user to the copy of this License.  Also, you must do one
+  of these things:
+
+      a) Accompany the work with the complete corresponding
+      machine-readable source code for the Library including whatever
+      changes were used in the work (which must be distributed under
+      Sections 1 and 2 above); and, if the work is an executable linked
+      with the Library, with the complete machine-readable "work that
+      uses the Library", as object code and/or source code, so that the
+      user can modify the Library and then relink to produce a modified
+      executable containing the modified Library.  (It is understood
+      that the user who changes the contents of definitions files in the
+      Library will not necessarily be able to recompile the application
+      to use the modified definitions.)
+
+      b) Use a suitable shared library mechanism for linking with the
+      Library.  A suitable mechanism is one that (1) uses at run time a
+      copy of the library already present on the user's computer system,
+      rather than copying library functions into the executable, and (2)
+      will operate properly with a modified version of the library, if
+      the user installs one, as long as the modified version is
+      interface-compatible with the version that the work was made with.
+
+      c) Accompany the work with a written offer, valid for at
+      least three years, to give the same user the materials
+      specified in Subsection 6a, above, for a charge no more
+      than the cost of performing this distribution.
+
+      d) If distribution of the work is made by offering access to copy
+      from a designated place, offer equivalent access to copy the above
+      specified materials from the same place.
+
+      e) Verify that the user has already received a copy of these
+      materials or that you have already sent this user a copy.
+
+    For an executable, the required form of the "work that uses the
+  Library" must include any data and utility programs needed for
+  reproducing the executable from it.  However, as a special exception,
+  the materials to be distributed need not include anything that is
+  normally distributed (in either source or binary form) with the major
+  components (compiler, kernel, and so on) of the operating system on
+  which the executable runs, unless that component itself accompanies
+  the executable.
+
+    It may happen that this requirement contradicts the license
+  restrictions of other proprietary libraries that do not normally
+  accompany the operating system.  Such a contradiction means you cannot
+  use both them and the Library together in an executable that you
+  distribute.
+
+    7. You may place library facilities that are a work based on the
+  Library side-by-side in a single library together with other library
+  facilities not covered by this License, and distribute such a combined
+  library, provided that the separate distribution of the work based on
+  the Library and of the other library facilities is otherwise
+  permitted, and provided that you do these two things:
+
+      a) Accompany the combined library with a copy of the same work
+      based on the Library, uncombined with any other library
+      facilities.  This must be distributed under the terms of the
+      Sections above.
+
+      b) Give prominent notice with the combined library of the fact
+      that part of it is a work based on the Library, and explaining
+      where to find the accompanying uncombined form of the same work.
+
+    8. You may not copy, modify, sublicense, link with, or distribute
+  the Library except as expressly provided under this License.  Any
+  attempt otherwise to copy, modify, sublicense, link with, or
+  distribute the Library is void, and will automatically terminate your
+  rights under this License.  However, parties who have received copies,
+  or rights, from you under this License will not have their licenses
+  terminated so long as such parties remain in full compliance.
+
+    9. You are not required to accept this License, since you have not
+  signed it.  However, nothing else grants you permission to modify or
+  distribute the Library or its derivative works.  These actions are
+  prohibited by law if you do not accept this License.  Therefore, by
+  modifying or distributing the Library (or any work based on the
+  Library), you indicate your acceptance of this License to do so, and
+  all its terms and conditions for copying, distributing or modifying
+  the Library or works based on it.
+
+    10. Each time you redistribute the Library (or any work based on the
+  Library), the recipient automatically receives a license from the
+  original licensor to copy, distribute, link with or modify the Library
+  subject to these terms and conditions.  You may not impose any further
+  restrictions on the recipients' exercise of the rights granted herein.
+  You are not responsible for enforcing compliance by third parties with
+  this License.
+
+    11. If, as a consequence of a court judgment or allegation of patent
+  infringement or for any other reason (not limited to patent issues),
+  conditions are imposed on you (whether by court order, agreement or
+  otherwise) that contradict the conditions of this License, they do not
+  excuse you from the conditions of this License.  If you cannot
+  distribute so as to satisfy simultaneously your obligations under this
+  License and any other pertinent obligations, then as a consequence you
+  may not distribute the Library at all.  For example, if a patent
+  license would not permit royalty-free redistribution of the Library by
+  all those who receive copies directly or indirectly through you, then
+  the only way you could satisfy both it and this License would be to
+  refrain entirely from distribution of the Library.
+
+  If any portion of this section is held invalid or unenforceable under any
+  particular circumstance, the balance of the section is intended to apply,
+  and the section as a whole is intended to apply in other circumstances.
+
+  It is not the purpose of this section to induce you to infringe any
+  patents or other property right claims or to contest validity of any
+  such claims; this section has the sole purpose of protecting the
+  integrity of the free software distribution system which is
+  implemented by public license practices.  Many people have made
+  generous contributions to the wide range of software distributed
+  through that system in reliance on consistent application of that
+  system; it is up to the author/donor to decide if he or she is willing
+  to distribute software through any other system and a licensee cannot
+  impose that choice.
+
+  This section is intended to make thoroughly clear what is believed to
+  be a consequence of the rest of this License.
+
+    12. If the distribution and/or use of the Library is restricted in
+  certain countries either by patents or by copyrighted interfaces, the
+  original copyright holder who places the Library under this License may add
+  an explicit geographical distribution limitation excluding those countries,
+  so that distribution is permitted only in or among countries not thus
+  excluded.  In such case, this License incorporates the limitation as if
+  written in the body of this License.
+
+    13. The Free Software Foundation may publish revised and/or new
+  versions of the Lesser General Public License from time to time.
+  Such new versions will be similar in spirit to the present version,
+  but may differ in detail to address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the Library
+  specifies a version number of this License which applies to it and
+  "any later version", you have the option of following the terms and
+  conditions either of that version or of any later version published by
+  the Free Software Foundation.  If the Library does not specify a
+  license version number, you may choose any version ever published by
+  the Free Software Foundation.
+
+    14. If you wish to incorporate parts of the Library into other free
+  programs whose distribution conditions are incompatible with these,
+  write to the author to ask for permission.  For software which is
+  copyrighted by the Free Software Foundation, write to the Free
+  Software Foundation; we sometimes make exceptions for this.  Our
+  decision will be guided by the two goals of preserving the free status
+  of all derivatives of our free software and of promoting the sharing
+  and reuse of software generally.
+
+            NO WARRANTY
+
+    15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+  WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+  OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+  PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+  LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+  THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+    16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+  WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+  AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+  FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+  CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+  LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+  RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+  FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+  SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+  DAMAGES.
+
+           END OF TERMS AND CONDITIONS
+
+             How to Apply These Terms to Your New Libraries
+
+    If you develop a new library, and you want it to be of the greatest
+  possible use to the public, we recommend making it free software that
+  everyone can redistribute and change.  You can do so by permitting
+  redistribution under these terms (or, alternatively, under the terms of the
+  ordinary General Public License).
+
+    To apply these terms, attach the following notices to the library.  It is
+  safest to attach them to the start of each source file to most effectively
+  convey the exclusion of warranty; and each file should have at least the
+  "copyright" line and a pointer to where the full notice is found.
+
+      <one line to give the library's name and a brief idea of what it does.>
+      Copyright (C) <year>  <name of author>
+
+      This library is free software; you can redistribute it and/or
+      modify it under the terms of the GNU Lesser General Public
+      License as published by the Free Software Foundation; either
+      version 2.1 of the License, or (at your option) any later version.
+
+      This library is distributed in the hope that it will be useful,
+      but WITHOUT ANY WARRANTY; without even the implied warranty of
+      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+      Lesser General Public License for more details.
+
+      You should have received a copy of the GNU Lesser General Public
+      License along with this library; if not, write to the Free Software
+      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+  Also add information on how to contact you by electronic and paper mail.
+
+  You should also get your employer (if you work as a programmer) or your
+  school, if any, to sign a "copyright disclaimer" for the library, if
+  necessary.  Here is a sample; alter the names:
+
+    Yoyodyne, Inc., hereby disclaims all copyright interest in the
+    library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+    <signature of Ty Coon>, 1 April 1990
+    Ty Coon, President of Vice
+
+  That's all there is to it!
diff --git a/MANIFEST.MF b/MANIFEST.MF
new file mode 100644
index 0000000..ba6fb81
--- /dev/null
+++ b/MANIFEST.MF
@@ -0,0 +1,2 @@
+Implementation-Title: JNA-POSIX
+Implementation-Version: 0.5
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f6d9b54
--- /dev/null
+++ b/README.md
@@ -0,0 +1,9 @@
+Java Native Runtime - POSIX
+===========================
+
+[![Build Status](https://travis-ci.org/jnr/jnr-posix.svg?branch=master)](https://travis-ci.org/jnr/jnr-posix)
+[![Windows Build Status](https://ci.appveyor.com/api/projects/status/ywxa3ihwr6r8mlrs/branch/master?svg=true)](https://ci.appveyor.com/project/jnr/jnr-posix/branch/master)
+Overview
+--------
+
+jnr-posix is a lightweight cross-platform POSIX emulation layer for Java, written in Java and is part of the JNR project (http://github.com/jnr)
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..2167919
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,248 @@
+<?xml version="1.0" ?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.sonatype.oss</groupId>
+    <artifactId>oss-parent</artifactId>
+    <version>7</version>
+  </parent>
+
+  <groupId>com.github.jnr</groupId>
+  <artifactId>jnr-posix</artifactId>
+  <packaging>jar</packaging>
+  <version>3.1.21-SNAPSHOT</version>
+  <name>jnr-posix</name>
+  <description>
+    Common cross-project/cross-platform POSIX APIs
+  </description>
+
+  <issueManagement>
+    <system>JIRA</system>
+    <url>http://jira.codehaus.org/browse/JRUBY</url>
+  </issueManagement>
+
+  <scm>
+    <connection>scm:git:git@github.com:jnr/jnr-posix.git</connection>
+    <developerConnection>scm:git:git@github.com:jnr/jnr-posix.git</developerConnection>
+    <url>git@github.com:jnr/jnr-posix.git</url>
+  </scm>
+
+  <licenses>
+    <license>
+      <name>Eclipse Public License - v 2.0</name>
+      <url>https://www.eclipse.org/legal/epl-2.0/</url>
+      <distribution>repo</distribution>
+    </license>
+    <license>
+      <name>GNU General Public License Version 2</name>
+      <url>http://www.gnu.org/copyleft/gpl.html</url>
+      <distribution>repo</distribution>
+    </license>
+    <license>
+      <name>GNU Lesser General Public License Version 2.1</name>
+      <url>http://www.gnu.org/licenses/lgpl.html</url>
+      <distribution>repo</distribution>
+    </license>
+  </licenses>
+
+  <developers>
+    <developer>
+      <id>enebo</id>
+      <name>Thomas E Enebo</name>
+      <email>tom.enebo@gmail.com</email>
+    </developer>
+    <developer>
+      <id>wmeissner</id>
+      <name>Wayne Meissner</name>
+      <email>wmeissner@gmail.com</email>
+    </developer>
+    <developer>
+      <id>headius</id>
+      <name>Charles Oliver Nutter</name>
+      <email>headius@headius.com</email>
+    </developer>
+  </developers>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <maven.compiler.source>8</maven.compiler.source>
+    <maven.compiler.target>8</maven.compiler.target>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.13.1</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.github.jnr</groupId>
+      <artifactId>jnr-ffi</artifactId>
+      <version>2.2.17</version>
+    </dependency>
+    <dependency>
+      <groupId>com.github.jnr</groupId>
+      <artifactId>jnr-constants</artifactId>
+      <version>0.10.4</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <extensions>
+      <extension>
+        <groupId>org.apache.maven.wagon</groupId>
+        <artifactId>wagon-webdav-jackrabbit</artifactId>
+        <version>1.0-beta-7</version>
+      </extension>
+    </extensions>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <version>2.3.7</version>
+        <configuration>
+          <instructions>
+            <Implementation-Title>JNA-POSIX</Implementation-Title>
+            <Implementation-Version>0.5</Implementation-Version>
+            <_nouses>true</_nouses>
+            <Import-Package>!sun.misc,*</Import-Package>
+          </instructions>
+        </configuration>
+        <executions>
+          <execution>
+            <id>bundle-manifest</id>
+            <phase>process-classes</phase>
+            <goals>
+              <goal>manifest</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <version>2.3.1</version>
+        <configuration>
+          <archive>
+            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+            <manifestEntries>
+              <Automatic-Module-Name>org.jnrproject.posix</Automatic-Module-Name>
+            </manifestEntries>
+          </archive>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <version>2.2.1</version>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+        <version>3.2.0</version>
+        <executions>
+          <execution>
+            <id>attach-javadocs</id>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <version>3.8.1</version>
+      </plugin>
+    </plugins>
+  </build>
+  <profiles>
+    <profile>
+      <id>non-windows-unit-tests</id>
+      <activation><os><family>!windows</family></os></activation>
+      <build>
+      <plugins>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-surefire-plugin</artifactId>
+          <version>2.18.1</version>
+          <configuration>
+            <forkCount>2</forkCount>
+            <reuseForks>false</reuseForks>
+            <includes>
+              <include>**/*Test.java</include>
+            </includes>
+            <excludes>
+              <exclude>**/windows/*Test.java</exclude>
+            </excludes>
+          </configuration>
+        </plugin>
+      </plugins>
+      </build>
+    </profile>
+    <profile>
+      <id>windows-unit-tests</id>
+      <activation><os><family>windows</family></os></activation>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-surefire-plugin</artifactId>
+            <version>2.18.1</version>
+            <configuration>
+              <forkCount>2</forkCount>
+              <reuseForks>false</reuseForks>
+            </configuration>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+    <profile>
+      <id>java9</id>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-compiler-plugin</artifactId>
+            <executions>
+              <execution>
+                <id>default-compile</id>
+                <phase>compile</phase>
+                <goals><goal>compile</goal></goals>
+                <configuration>
+                  <includes>
+                    <include>jnr/posix/util/SunMiscSignal.java</include>
+                  </includes>
+                </configuration>
+              </execution>
+              <execution>
+                <id>java9-compile</id>
+                <phase>compile</phase>
+                <goals><goal>compile</goal></goals>
+                <configuration>
+                  <!-- Use -release compiler option rather than source/target if 9+ -->
+                  <release>${maven.compiler.target}</release>
+                  <excludes>
+                    <exclude>jnr/posix/util/SunMiscSignal.java</exclude>
+                  </excludes>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+      <activation>
+        <jdk>[9,)</jdk>
+      </activation>
+    </profile>
+  </profiles>
+</project>
diff --git a/src/main/java/jnr/posix/AbstractJavaFileStat.java b/src/main/java/jnr/posix/AbstractJavaFileStat.java
new file mode 100644
index 0000000..fae6bde
--- /dev/null
+++ b/src/main/java/jnr/posix/AbstractJavaFileStat.java
@@ -0,0 +1,133 @@
+package jnr.posix;
+
+public abstract class AbstractJavaFileStat implements FileStat {
+    protected final POSIXHandler handler;
+    protected final POSIX posix;
+
+    public AbstractJavaFileStat(POSIX posix, POSIXHandler handler) {
+        this.handler = handler;
+        this.posix = posix;
+    }
+
+    public boolean isBlockDev() {
+        handler.unimplementedError("block device detection");
+
+        return false;
+    }
+
+    /**
+     * Limitation: [see JRUBY-1516] We just pick more likely value.  This is a little scary.
+     */
+    public boolean isCharDev() {
+        return false;
+    }
+
+    public boolean isFifo() {
+        handler.unimplementedError("fifo file detection");
+
+        return false;
+    }
+
+    public boolean isNamedPipe() {
+        handler.unimplementedError("piped file detection");
+
+        return false;
+    }
+
+    public boolean isSetgid() {
+        handler.unimplementedError("setgid detection");
+
+        return false;
+    }
+
+    public boolean isSetuid() {
+        handler.unimplementedError("setuid detection");
+
+        return false;
+    }
+
+    public boolean isSocket() {
+        handler.unimplementedError("socket file type detection");
+
+        return false;
+    }
+
+    public boolean isSticky() {
+        handler.unimplementedError("sticky bit detection");
+
+        return false;
+    }
+
+    public int major(long dev) {
+        handler.unimplementedError("major device");
+
+        return -1;
+    }
+
+    public int minor(long dev) {
+        handler.unimplementedError("minor device");
+
+        return -1;
+    }
+
+    public int nlink() {
+        handler.unimplementedError("stat.nlink");
+
+        return -1;
+    }
+
+    public long rdev() {
+        handler.unimplementedError("stat.rdev");
+
+        return -1;
+    }
+
+    // Limitation: We have no pure-java way of getting uid. RubyZip needs this defined to work.
+    public int uid() {
+        return -1;
+    }
+
+    public long blocks() {
+        handler.unimplementedError("stat.st_blocks");
+
+        return -1;
+    }
+
+    public long blockSize() {
+        // Limitation: We cannot determine, so always return 4096 (better than blowing up)
+        return 4096;
+    }
+
+    public long dev() {
+        handler.unimplementedError("stat.st_dev");
+
+        return -1;
+    }
+
+    public String ftype() {
+        if (isFile()) {
+            return "file";
+        } else if (isDirectory()) {
+            return "directory";
+        }
+
+        return "unknown";
+    }
+
+    public int gid() {
+        handler.unimplementedError("stat.st_gid");
+
+        return -1;
+    }
+
+    public boolean groupMember(int gid) {
+        return posix.getgid() == gid || posix.getegid() == gid;
+    }
+
+    /**
+     *  Limitation: We have no pure-java way of getting inode.  webrick needs this defined to work.
+     */
+    public long ino() {
+        return 0;
+    }
+}
diff --git a/src/main/java/jnr/posix/AixFileStat.java b/src/main/java/jnr/posix/AixFileStat.java
new file mode 100644
index 0000000..c797373
--- /dev/null
+++ b/src/main/java/jnr/posix/AixFileStat.java
@@ -0,0 +1,150 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: EPL 2.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Eclipse Public
+ * License Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+/**
+ * $Id: $
+ */
+
+package jnr.posix;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.StructLayout;
+
+/**
+ * This corresponds with struct stat64x on AIX
+ */
+public final class AixFileStat extends BaseFileStat implements NanosecondFileStat {
+    private static final class Layout extends StructLayout {
+
+        private Layout(jnr.ffi.Runtime runtime) {
+            super(runtime);
+        }
+
+        public final Unsigned64 st_dev = new Unsigned64();
+        public final Signed64  st_ino = new Signed64();
+        public final Unsigned32  st_mode = new Unsigned32();
+        public final Signed16  st_nlink = new Signed16();
+        public final Unsigned16 st_flag = new Unsigned16();
+        public final Unsigned32 st_uid = new Unsigned32();
+        public final Unsigned32 st_gid = new Unsigned32();
+        public final Unsigned64  st_rdev = new Unsigned64();
+        public final Signed64  st_size = new Signed64();
+        public final Signed64 st_atime = new Signed64();
+        public final Signed32 st_atime_n = new Signed32();
+        public final Signed32 st_pad1 = new Signed32();
+
+        public final Signed64 st_mtime = new Signed64();
+        public final Signed32 st_mtime_n = new Signed32();
+        public final Signed32 st_pad2 = new Signed32();
+
+        public final Signed64 st_ctime = new Signed64();
+        public final Signed32 st_ctime_n = new Signed32();
+        public final Signed32 st_pad3 = new Signed32();
+
+        public final Unsigned64	st_blksize = new Unsigned64();
+        public final Unsigned64	st_blocks = new Unsigned64();
+        public final Signed32 st_vfstype = new Signed32();
+        
+        public final Unsigned32 st_vfs = new Unsigned32();
+        public final Unsigned32 st_type = new Unsigned32();
+        public final Unsigned32 st_gen = new Unsigned32();
+        public final Padding st_reserved = new Padding(NativeType.UINT, 11);
+        /* total size should be 176 bytes */
+    }
+    private static final Layout layout = new Layout(jnr.ffi.Runtime.getSystemRuntime());
+
+    public AixFileStat(NativePOSIX posix) {
+        super(posix, layout);
+    }
+
+    public long atime() {
+        return layout.st_atime.get(memory);
+    }
+
+    public long blocks() {
+        return layout.st_blocks.get(memory);
+    }
+
+    public long blockSize() {
+        return layout.st_blksize.get(memory);
+    }
+
+    public long ctime() {
+        return layout.st_ctime.get(memory);
+    }
+
+    public long dev() {
+        return layout.st_dev.get(memory);
+    }
+
+    public int gid() {
+        return (int) layout.st_gid.get(memory);
+    }
+
+    public long ino() {
+        return layout.st_ino.get(memory);
+    }
+
+    public int mode() {
+        return (int) layout.st_mode.get(memory) & 0xffff;
+    }
+
+    public long mtime() {
+        return layout.st_mtime.get(memory);
+    }
+
+    public int nlink() {
+        return (int) layout.st_nlink.get(memory);
+    }
+
+    public long rdev() {
+        return layout.st_rdev.get(memory);
+    }
+
+    public long st_size() {
+        return layout.st_size.get(memory);
+    }
+
+    public int uid() {
+        return (int) layout.st_uid.get(memory);
+    }
+
+    @Override
+    public long aTimeNanoSecs() {
+        return layout.st_atime_n.get(memory);
+    }
+
+    @Override
+    public long cTimeNanoSecs() {
+        return layout.st_ctime_n.get(memory);
+    }
+
+    @Override
+    public long mTimeNanoSecs() {
+        return layout.st_mtime_n.get(memory);
+    }
+}
diff --git a/src/main/java/jnr/posix/AixFlock.java b/src/main/java/jnr/posix/AixFlock.java
new file mode 100644
index 0000000..75f8aa2
--- /dev/null
+++ b/src/main/java/jnr/posix/AixFlock.java
@@ -0,0 +1,55 @@
+package jnr.posix;
+
+public final class AixFlock extends Flock {
+    public final Signed16 l_type = new Signed16();
+    public final Signed16 l_whence = new Signed16();
+    public final Unsigned32 l_sysid = new Unsigned32();
+    public final Signed32 l_pid = new Signed32();
+    public final Signed32 l_vfs = new Signed32();
+    public final SignedLong l_start = new SignedLong();
+    public final SignedLong l_len = new SignedLong();
+
+    public AixFlock(jnr.ffi.Runtime runtime) {
+        super(runtime);
+    }
+
+    public void type(short type) {
+        this.l_type.set(type);
+    }
+
+    public void whence(short whence) {
+        this.l_whence.set(whence);
+    }
+
+    public void start(long start) {
+        this.l_start.set(start);
+    }
+
+    public void len(long len) {
+        this.l_len.set(len);
+    }
+
+    public void pid(int pid) {
+        this.l_pid.set(pid);
+    }
+
+    public short type() {
+        return this.l_type.get();
+    }
+
+    public short whence() {
+        return this.l_whence.get();
+    }
+
+    public long start() {
+        return this.l_start.get();
+    }
+
+    public long len() {
+        return this.l_len.get();
+    }
+
+    public int pid() {
+        return this.l_pid.get();
+    }
+}
diff --git a/src/main/java/jnr/posix/AixLibC.java b/src/main/java/jnr/posix/AixLibC.java
new file mode 100644
index 0000000..b84abe4
--- /dev/null
+++ b/src/main/java/jnr/posix/AixLibC.java
@@ -0,0 +1,38 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: EPL 2.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Eclipse Public
+ * License Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+/**
+ * $Id: $
+ */
+
+package jnr.posix;
+
+public interface AixLibC extends UnixLibC {
+    public int stat64x(CharSequence path, AixFileStat stat);
+    public int fstat64x(int fd, AixFileStat stat);
+    public int lstat64x(CharSequence path, AixFileStat stat);
+}
diff --git a/src/main/java/jnr/posix/AixPOSIX.java b/src/main/java/jnr/posix/AixPOSIX.java
new file mode 100644
index 0000000..cb2d023
--- /dev/null
+++ b/src/main/java/jnr/posix/AixPOSIX.java
@@ -0,0 +1,149 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: EPL 2.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Eclipse Public
+ * License Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+/**
+ * $Id: $
+ */
+
+package jnr.posix;
+
+import jnr.constants.platform.Sysconf;
+import jnr.constants.platform.Fcntl;
+import jnr.ffi.Memory;
+import jnr.ffi.Pointer;
+import jnr.ffi.mapper.FromNativeContext;
+import jnr.posix.util.MethodName;
+
+import java.io.FileDescriptor;
+import java.nio.ByteBuffer;
+import jnr.constants.platform.Confstr;
+import jnr.constants.platform.Pathconf;
+
+final class AixPOSIX extends BaseNativePOSIX {
+    // These should probably be put into jnr-constants instead eventually, but
+    // they're here for now as a one-off to work around AIX flock issues
+    private enum FlockFlags {
+        LOCK_SH(1),
+        LOCK_EX(2),
+        LOCK_NB(4),
+        LOCK_UN(8);
+        private final int value;
+        FlockFlags(int value) {
+            this.value = value;
+        }
+        public final int intValue() {
+            return value;
+        }
+    }
+
+    AixPOSIX(LibCProvider libc, POSIXHandler handler) {
+        super(libc, handler);
+    }
+
+    public FileStat allocateStat() { 
+        return new AixFileStat(this); 
+    }
+
+    public MsgHdr allocateMsgHdr() {
+        handler.unimplementedError(MethodName.getCallerMethodName());
+        return null;
+    }
+
+    public SocketMacros socketMacros() {
+        handler.unimplementedError(MethodName.getCallerMethodName());
+        return null;
+    }
+
+    public long sysconf(Sysconf name) {
+        return libc().sysconf(name);
+    }
+
+    public int confstr(Confstr name, ByteBuffer buf, int len) {
+        return libc().confstr(name, buf, len);
+    }
+
+    public int fpathconf(int fd, Pathconf name) {
+        return libc().fpathconf(fd, name);
+    }
+
+    public Times times() {
+        return NativeTimes.times(this);
+    }
+
+
+    public static final PointerConverter PASSWD = new PointerConverter() {
+        public Object fromNative(Object arg, FromNativeContext ctx) {
+            return arg != null ? new AixPasswd((Pointer) arg) : null;
+        }
+    };
+
+    public Pointer allocatePosixSpawnFileActions() {
+        return Memory.allocateDirect(getRuntime(), 4);
+    }
+
+    public Pointer allocatePosixSpawnattr() {
+        return Memory.allocateDirect(getRuntime(), 60);
+    }
+
+    // AIX flock lives in libbsd instead of libc.  AIX flock locks fully
+    // interact with fcntl locks, so we can implement flock in terms of fcntl,
+    // which is what we do here to avoid having to pull in that lib.
+    @Override
+    public int flock(int fd, int operation) {
+        int cmd = Fcntl.F_SETLKW.intValue();
+        short type = 0;
+
+        // Map the flock call flags to a fcntl flock type flag
+        if ((operation & FlockFlags.LOCK_SH.intValue()) != 0) {
+            type = (short)Fcntl.F_RDLCK.intValue();
+        } else if ((operation & FlockFlags.LOCK_EX.intValue()) != 0) {
+            type = (short)Fcntl.F_WRLCK.intValue();
+        } else if ((operation & FlockFlags.LOCK_UN.intValue()) != 0) {
+            type = (short)Fcntl.F_UNLCK.intValue();
+        }
+
+        // Switch to the fcntl non-blocking command
+        if ((operation & FlockFlags.LOCK_NB.intValue()) != 0) {
+            cmd = Fcntl.F_SETLK.intValue();
+        }
+
+        Flock flock = allocateFlock();
+        flock.type(type);
+        flock.whence((short)0);
+        flock.start(0);
+        flock.len(0);
+        return libc().fcntl(fd, cmd, flock);
+    }
+
+    @Override
+    public Timeval allocateTimeval() { return new AixTimeval(getRuntime()); }
+
+    // This isn't an override yet, because Flock would have to be implemented
+    // for all platforms, or at least with a DefaultNative implementation.  This
+    // is fine for now, because only AIX uses the flock structure
+    public Flock allocateFlock() { return new AixFlock(getRuntime()); }
+}
diff --git a/src/main/java/jnr/posix/AixPasswd.java b/src/main/java/jnr/posix/AixPasswd.java
new file mode 100644
index 0000000..b76a4b8
--- /dev/null
+++ b/src/main/java/jnr/posix/AixPasswd.java
@@ -0,0 +1,97 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: EPL 2.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Eclipse Public
+ * License Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+/**
+ * $Id: $
+ */
+
+package jnr.posix;
+
+
+import jnr.ffi.StructLayout;
+
+public class AixPasswd extends NativePasswd implements Passwd {
+    private static final class Layout extends StructLayout {
+
+        private Layout(jnr.ffi.Runtime runtime) {
+            super(runtime);
+        }
+
+        public final UTF8StringRef pw_name = new UTF8StringRef();   // user name
+        public final UTF8StringRef pw_passwd = new UTF8StringRef(); // password (encrypted)
+        public final uid_t pw_uid = new uid_t();       // user id
+        public final gid_t pw_gid = new gid_t();       // user id
+        public final UTF8StringRef pw_gecos = new UTF8StringRef();  // login info
+        public final UTF8StringRef pw_dir = new UTF8StringRef();    // home directory
+        public final UTF8StringRef pw_shell = new UTF8StringRef();  // default shell
+    }
+    private static final Layout layout = new Layout(jnr.ffi.Runtime.getSystemRuntime());
+
+    AixPasswd(jnr.ffi.Pointer memory) {
+        super(memory);
+    }
+
+    public String getAccessClass() {
+        return "unknown";
+    }
+
+    public String getGECOS() {
+        return layout.pw_gecos.get(memory);
+    }
+
+    public long getGID() {
+        return layout.pw_gid.get(memory);
+    }
+
+    public String getHome() {
+        return layout.pw_dir.get(memory);
+    }
+
+    public String getLoginName() {
+        return layout.pw_name.get(memory);
+    }
+
+    public int getPasswdChangeTime() {
+        return 0;
+    }
+
+    public String getPassword() {
+        return layout.pw_passwd.get(memory);
+    }
+
+    public String getShell() {
+        return layout.pw_shell.get(memory);
+    }
+
+    public long getUID() {
+        return layout.pw_uid.get(memory);
+    }
+
+    public int getExpire() {
+        return Integer.MAX_VALUE;
+    }
+}
diff --git a/src/main/java/jnr/posix/AixTimeval.java b/src/main/java/jnr/posix/AixTimeval.java
new file mode 100644
index 0000000..cbc0ece
--- /dev/null
+++ b/src/main/java/jnr/posix/AixTimeval.java
@@ -0,0 +1,32 @@
+package jnr.posix;
+
+public final class AixTimeval extends Timeval {
+    public final SignedLong tv_sec = new SignedLong();
+    public final Signed32 tv_usec = new Signed32();
+
+    public AixTimeval(jnr.ffi.Runtime runtime) {
+        super(runtime);
+    }
+
+    public void setTime(long[] timeval) {
+        assert timeval.length == 2;
+        tv_sec.set(timeval[0]);
+        tv_usec.set((int)timeval[1]);
+    }
+
+    public void sec(long sec) {
+        this.tv_sec.set(sec);
+    }
+
+    public void usec(long usec) {
+        this.tv_usec.set((int)usec);
+    }
+
+    public long sec() {
+        return tv_sec.get();
+    }
+
+    public long usec() {
+        return tv_usec.get();
+    }
+}
diff --git a/src/main/java/jnr/posix/BaseCmsgHdr.java b/src/main/java/jnr/posix/BaseCmsgHdr.java
new file mode 100644
index 0000000..c0148fc
--- /dev/null
+++ b/src/main/java/jnr/posix/BaseCmsgHdr.java
@@ -0,0 +1,50 @@
+package jnr.posix;
+
+import jnr.ffi.Memory;
+import jnr.ffi.Pointer;
+
+import java.nio.ByteBuffer;
+
+/**
+ * @author Bob McWhirter
+ */
+abstract class BaseCmsgHdr implements CmsgHdr {
+
+    protected final NativePOSIX posix;
+    final Pointer memory;
+
+    protected BaseCmsgHdr(NativePOSIX posix, Pointer memory) {
+        this.posix = posix;
+        this.memory = memory;
+    }
+
+    protected BaseCmsgHdr(NativePOSIX posix, Pointer memory, int totalLen) {
+        this.posix = posix;
+        this.memory = memory;
+        setLen( totalLen );
+    }
+
+    public void setData(ByteBuffer data) {
+        byte[] bytes = new byte[data.capacity() - data.position()];
+        data.get(bytes);
+        posix.socketMacros().CMSG_DATA(this.memory).put(0, bytes, 0, bytes.length);
+    }
+
+    public ByteBuffer getData() {
+        int dataLen =  getLen() - posix.socketMacros().CMSG_LEN(0);
+        if ( dataLen == 0 ) {
+            return null;
+        }
+        byte[] bytes = new byte[dataLen];
+
+        posix.socketMacros().CMSG_DATA(this.memory).get(0, bytes, 0, bytes.length);
+
+        ByteBuffer buf = ByteBuffer.allocate(bytes.length);
+        buf.put(bytes);
+        buf.flip();
+        return buf;
+    }
+
+    abstract void setLen(int len);
+
+}
diff --git a/src/main/java/jnr/posix/BaseFileStat.java b/src/main/java/jnr/posix/BaseFileStat.java
new file mode 100644
index 0000000..34fd10b
--- /dev/null
+++ b/src/main/java/jnr/posix/BaseFileStat.java
@@ -0,0 +1,193 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: EPL 2.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Eclipse Public
+ * License Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * 
+ *  
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+/**
+ * $Id: $
+ */
+
+package jnr.posix;
+
+import jnr.ffi.Memory;
+import jnr.ffi.Pointer;
+import jnr.ffi.StructLayout;
+
+/**
+ *
+ */
+public abstract class BaseFileStat implements FileStat {
+    protected final POSIX posix;
+    protected final Pointer memory;
+
+    protected BaseFileStat(NativePOSIX posix, StructLayout layout) {
+        this.posix = posix;
+        this.memory = Memory.allocate(posix.getRuntime(), layout.size());
+    }
+    
+    public java.lang.String ftype() {
+        if (isFile()) {
+            return "file";
+        } else if (isDirectory()) {
+            return "directory";
+        } else if (isCharDev()) {
+            return "characterSpecial";
+        } else if (isBlockDev()) {
+            return "blockSpecial";
+        } else if (isFifo()) {
+            return "fifo";
+        } else if (isSymlink()) {
+            return "link";
+        } else if (isSocket()) {
+            return "socket";
+        } 
+          
+        return "unknown";
+    }
+
+    public boolean groupMember(int gid) {
+        if (posix.getgid() == gid || posix.getegid() == gid) {
+            return true;
+        }
+
+        // FIXME: Though not Posix, windows has different mechanism for this.
+        
+        return false;
+    }
+    
+    public boolean isBlockDev() {
+        return (mode() & S_IFMT) == S_IFBLK;
+    }
+    
+    public boolean isCharDev() {
+        return (mode() & S_IFMT) == S_IFCHR;
+    }
+
+    public boolean isDirectory() {
+        return (mode() & S_IFMT) == S_IFDIR;
+    }
+    
+    public boolean isEmpty() {
+        return st_size() == 0;
+    }
+
+    public boolean isExecutable() {
+        if (posix.geteuid() == 0) return (mode() & S_IXUGO) != 0;
+        if (isOwned()) return (mode() & S_IXUSR) != 0;
+        if (isGroupOwned()) return (mode() & S_IXGRP) != 0;
+        return (mode() & S_IXOTH) != 0;
+    }
+    
+    public boolean isExecutableReal() {
+        if (posix.getuid() == 0) return (mode() & S_IXUGO) != 0;
+        if (isROwned()) return (mode() & S_IXUSR) != 0;
+        if (groupMember(gid())) return (mode() & S_IXGRP) != 0;
+        return (mode() & S_IXOTH) != 0;
+    }
+    
+    public boolean isFile() {
+        return (mode() & S_IFMT) == S_IFREG;
+    }
+
+    public boolean isFifo() {
+        return (mode() & S_IFMT) == S_IFIFO;
+    }
+    
+    public boolean isGroupOwned() {
+        return groupMember(gid());
+    }
+
+    public boolean isIdentical(FileStat other) {
+        return dev() == other.dev() && ino() == other.ino(); 
+    }
+
+    public boolean isNamedPipe() {
+        return (mode() & S_IFIFO) != 0;
+    }
+    
+    public boolean isOwned() {
+        return posix.geteuid() == uid();
+    }
+    
+    public boolean isROwned() {
+        return posix.getuid() == uid();
+    }
+    
+    public boolean isReadable() {
+        if (posix.geteuid() == 0) return true;
+        if (isOwned()) return (mode() & S_IRUSR) != 0;
+        if (isGroupOwned()) return (mode() & S_IRGRP) != 0;
+        return (mode() & S_IROTH) != 0;
+    }
+    
+    public boolean isReadableReal() {
+        if (posix.getuid() == 0) return true;
+        if (isROwned()) return (mode() & S_IRUSR) != 0;
+        if (groupMember(gid())) return (mode() & S_IRGRP) != 0;
+        return (mode() & S_IROTH) != 0;
+    }
+    
+    public boolean isSetgid() {
+        return (mode() & S_ISGID) != 0;
+    }
+
+    public boolean isSetuid() {
+        return (mode() & S_ISUID) != 0;
+    }
+
+    public boolean isSocket() {
+        return (mode() & S_IFMT) == S_IFSOCK;
+    }
+    
+    public boolean isSticky() {
+        return (mode() & S_ISVTX) != 0;
+    }
+
+    public boolean isSymlink() {
+        return (mode() & S_IFMT) == S_IFLNK;
+    }
+
+    public boolean isWritable() {
+        if (posix.geteuid() == 0) return true;
+        if (isOwned()) return (mode() & S_IWUSR) != 0;
+        if (isGroupOwned()) return (mode() & S_IWGRP) != 0;
+        return (mode() & S_IWOTH) != 0;
+    }
+
+    public boolean isWritableReal() {
+        if (posix.getuid() == 0) return true;
+        if (isROwned()) return (mode() & S_IWUSR) != 0;
+        if (groupMember(gid())) return (mode() & S_IWGRP) != 0;
+        return (mode() & S_IWOTH) != 0;
+    }
+
+    public int major(long dev) {
+        return (int) (dev >> 24) & 0xff;
+    }
+    
+    public int minor(long dev) {
+        return (int) (dev & 0xffffff);
+    }
+}
diff --git a/src/main/java/jnr/posix/BaseIovec.java b/src/main/java/jnr/posix/BaseIovec.java
new file mode 100644
index 0000000..0273096
--- /dev/null
+++ b/src/main/java/jnr/posix/BaseIovec.java
@@ -0,0 +1,70 @@
+package jnr.posix;
+
+import jnr.ffi.Runtime;
+import jnr.ffi.Memory;
+import jnr.ffi.Pointer;
+import jnr.ffi.StructLayout;
+
+import java.nio.ByteBuffer;
+
+/**
+ * @author Bob McWhirter
+ */
+public class BaseIovec implements Iovec {
+
+
+    public static class Layout extends StructLayout {
+        protected Layout(Runtime runtime) {
+            super(runtime);
+        }
+
+        public final Pointer iov_base = new Pointer();
+        public final size_t iov_len = new size_t();
+    }
+
+    public static final Layout layout = new Layout(Runtime.getSystemRuntime());
+
+    private final NativePOSIX posix;
+    protected final Pointer memory;
+
+    public String toString(String indent) {
+        StringBuffer buf = new StringBuffer();
+        buf.append( indent ).append( "iovec {\n" );
+        buf.append(indent).append( "  iov_base=" ).append(layout.iov_base.get(this.memory)).append( ",\n" );
+        buf.append( indent ).append( "  iov_len=" ).append( layout.iov_len.get( this.memory ) ).append(",\n");
+        buf.append(indent).append( "}" );
+
+        return buf.toString();
+    }
+
+    protected BaseIovec(NativePOSIX posix) {
+        this.posix = posix;
+        this.memory = Memory.allocate(posix.getRuntime(), layout.size());
+    }
+
+    BaseIovec(NativePOSIX posix, Pointer memory) {
+        this.posix = posix;
+        this.memory = memory;
+    }
+
+    public ByteBuffer get() {
+        int len = getLen();
+        byte[] bytes = new byte[len];
+        layout.iov_base.get( this.memory ).get(0, bytes, 0, len );
+        return ByteBuffer.wrap( bytes );
+    }
+
+    public void set(ByteBuffer buf) {
+        int len = buf.remaining();
+        layout.iov_base.set( this.memory, Pointer.wrap( posix.getRuntime(), buf ) );
+        setLen(len);
+    }
+
+    protected void setLen(int len) {
+        layout.iov_len.set( this.memory, len );
+    }
+
+    protected int getLen() {
+        return (int) layout.iov_len.get( this.memory );
+    }
+}
diff --git a/src/main/java/jnr/posix/BaseMsgHdr.java b/src/main/java/jnr/posix/BaseMsgHdr.java
new file mode 100644
index 0000000..a5398b8
--- /dev/null
+++ b/src/main/java/jnr/posix/BaseMsgHdr.java
@@ -0,0 +1,150 @@
+package jnr.posix;
+
+import jnr.ffi.Pointer;
+import jnr.ffi.Runtime;
+import jnr.ffi.StructLayout;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Bob McWhirter
+ */
+public abstract class BaseMsgHdr implements MsgHdr {
+
+    protected final NativePOSIX posix;
+    protected final Pointer memory;
+
+    protected BaseMsgHdr(NativePOSIX posix, StructLayout layout) {
+        this.posix = posix;
+        this.memory = posix.getRuntime().getMemoryManager().allocateTemporary(layout.size(), true);
+    }
+
+    public void setName(String name) {
+        if (name == null) {
+            setNamePointer(null);
+            setNameLen(0);
+            return;
+        }
+        byte[] nameBytes = name.getBytes(Charset.forName("US-ASCII"));
+        Pointer p = Runtime.getSystemRuntime().getMemoryManager().allocateTemporary(nameBytes.length, true);
+        p.put(0, nameBytes, 0, nameBytes.length);
+        setNamePointer(p);
+        setNameLen(nameBytes.length);
+    }
+
+    public String getName() {
+        Pointer ptr = getNamePointer();
+        if (ptr == null) {
+            return null;
+        }
+        return ptr.getString(0, getNameLen(), Charset.forName( "US-ASCII" ));
+    }
+
+    public CmsgHdr allocateControl(int dataLength) {
+        CmsgHdr[] controls = allocateControls(new int[]{dataLength});
+        return controls[0];
+    }
+
+    public CmsgHdr[] allocateControls(int[] dataLengths) {
+        CmsgHdr[] cmsgs = new CmsgHdr[dataLengths.length];
+
+        int totalSize = 0;
+        for (int i = 0; i < dataLengths.length; ++i) {
+            totalSize += posix.socketMacros().CMSG_SPACE(dataLengths[i]);
+        }
+
+        Pointer ptr = posix.getRuntime().getMemoryManager().allocateDirect(totalSize);
+
+        int offset = 0;
+        for (int i = 0; i < dataLengths.length; ++i) {
+            int eachSpace = posix.socketMacros().CMSG_SPACE(dataLengths[i]);
+            int len = posix.socketMacros().CMSG_LEN(dataLengths[i]);
+            CmsgHdr each = allocateCmsgHdrInternal(posix, ptr.slice(offset, eachSpace), len);
+            cmsgs[i] = each;
+            offset += eachSpace;
+        }
+
+        setControlPointer(ptr);
+        setControlLen(totalSize);
+
+        return cmsgs;
+    }
+
+    public CmsgHdr[] getControls() {
+        int len = getControlLen();
+        if (len == 0) {
+            return new CmsgHdr[0];
+        }
+
+        List<CmsgHdr> control = new ArrayList<CmsgHdr>();
+
+        int offset = 0;
+
+        Pointer controlPtr = getControlPointer();
+
+        while (offset < len) {
+            CmsgHdr each = allocateCmsgHdrInternal(posix, controlPtr.slice(offset), -1);
+            offset += posix.socketMacros().CMSG_SPACE(each.getLen());
+            control.add(each);
+        }
+
+        return control.toArray(new CmsgHdr[control.size()]);
+    }
+
+    public void setIov(ByteBuffer[] buffers) {
+        Pointer iov = Runtime.getSystemRuntime().getMemoryManager().allocateDirect(BaseIovec.layout.size() * buffers.length);
+
+        for (int i = 0; i < buffers.length; ++i) {
+            Pointer eachIovecPtr = iov.slice(BaseIovec.layout.size() * i);
+            BaseIovec eachIovec = new BaseIovec(posix, eachIovecPtr);
+            eachIovec.set(buffers[i]);
+        }
+
+        setIovPointer(iov);
+        setIovLen(buffers.length);
+    }
+
+    public ByteBuffer[] getIov() {
+        int len = getIovLen();
+
+        ByteBuffer[] buffers = new ByteBuffer[len];
+
+        Pointer iov = getIovPointer();
+
+        for (int i = 0; i < len; ++i) {
+            Pointer eachPtr = iov.slice(BaseIovec.layout.size() * i);
+            BaseIovec eachIov = new BaseIovec(posix, eachPtr);
+            buffers[i] = eachIov.get();
+        }
+
+        return buffers;
+    }
+
+    abstract void setNamePointer(Pointer name);
+
+    abstract Pointer getNamePointer();
+
+    abstract void setNameLen(int len);
+
+    abstract int getNameLen();
+
+    abstract void setIovPointer(Pointer iov);
+
+    abstract Pointer getIovPointer();
+
+    abstract int getIovLen();
+
+    abstract void setIovLen(int len);
+
+    abstract CmsgHdr allocateCmsgHdrInternal(NativePOSIX posix, Pointer pointer, int len);
+
+    abstract void setControlPointer(Pointer control);
+
+    abstract Pointer getControlPointer();
+
+    abstract void setControlLen(int len);
+
+}
diff --git a/src/main/java/jnr/posix/BaseNativePOSIX.java b/src/main/java/jnr/posix/BaseNativePOSIX.java
new file mode 100644
index 0000000..96ed7de
--- /dev/null
+++ b/src/main/java/jnr/posix/BaseNativePOSIX.java
@@ -0,0 +1,944 @@
+package jnr.posix;
+
+import jnr.constants.Constant;
+import jnr.constants.platform.Errno;
+import jnr.constants.platform.Fcntl;
+import jnr.constants.platform.Signal;
+import jnr.constants.platform.Sysconf;
+import jnr.ffi.*;
+import jnr.ffi.byref.NumberByReference;
+import jnr.ffi.mapper.FromNativeContext;
+import jnr.ffi.mapper.FromNativeConverter;
+import jnr.ffi.mapper.ToNativeContext;
+import jnr.ffi.mapper.ToNativeConverter;
+import jnr.posix.util.Java5ProcessMaker;
+import jnr.posix.util.MethodName;
+import jnr.posix.util.ProcessMaker;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import jnr.constants.platform.Confstr;
+import jnr.constants.platform.Pathconf;
+import jnr.ffi.annotations.Out;
+
+public abstract class BaseNativePOSIX extends NativePOSIX implements POSIX {
+    private final LibC libc;
+    private final Crypt crypt;
+    
+    protected final POSIXHandler handler;
+    protected final JavaLibCHelper helper;
+    
+    protected final Map<Signal, SignalHandler> signalHandlers = new HashMap();
+    
+    protected BaseNativePOSIX(LibCProvider libcProvider, POSIXHandler handler) {
+        this.handler = handler;
+        this.libc = libcProvider.getLibC();
+        this.crypt = libcProvider.getCrypt();
+        this.helper = new JavaLibCHelper(handler);
+    }
+
+    public ProcessMaker newProcessMaker(String... command) {
+        return new Java5ProcessMaker(handler, command);
+    }
+
+    public ProcessMaker newProcessMaker() {
+        return new Java5ProcessMaker(handler);
+    }
+
+    public final LibC libc() {
+        return libc;
+    }
+
+    public final Crypt crypt() {
+        return crypt;
+    }
+
+    POSIXHandler handler() {
+        return handler;
+    }
+
+    protected <T> T unimplementedNull() {
+        handler().unimplementedError(MethodName.getCallerMethodName());
+        return null;
+    }
+
+    protected int unimplementedInt() {
+        handler().unimplementedError(MethodName.getCallerMethodName());
+        return -1;
+    }
+
+    public int chmod(String filename, int mode) {
+        return libc().chmod(filename, mode);
+    }
+
+    public int fchmod(int fd, int mode) {
+        return libc().fchmod(fd, mode);
+    }
+
+    public int chown(String filename, int user, int group) {
+        return libc().chown(filename, user, group);
+    }
+
+    public int fchown(int fd, int user, int group) {
+        return libc().fchown(fd, user, group);
+    }
+
+    public CharSequence crypt(CharSequence key, CharSequence salt) {
+        Crypt crypt = crypt();
+
+        if (crypt == null) {
+            return JavaLibCHelper.crypt(key, salt);
+        }
+
+        return crypt.crypt(key, salt);
+    }
+
+    public byte[] crypt(byte[] key, byte[] salt) {
+        Crypt crypt = crypt();
+
+        if (crypt == null) {
+            return JavaLibCHelper.crypt(key, salt);
+        }
+
+        Pointer ptr = crypt().crypt(key, salt);
+        if (ptr == null) return null;
+        int end = ptr.indexOf(0, (byte)0);
+        byte[] bytes = new byte[end + 1];
+        ptr.get(0, bytes, 0, end);
+
+        return bytes;
+    }
+
+    public int exec(String path, String... args) {
+        handler.unimplementedError("exec unimplemented");
+        return -1;
+    }
+    
+    public int exec(String path, String[] args, String[] envp) {
+        handler.unimplementedError("exec unimplemented");
+        return -1;
+    }
+    
+    public int execv(String path, String[] args) {
+        return libc().execv(path, args);
+    }
+    
+    public int execve(String path, String[] args, String[] env) {
+        return libc().execve(path, args, env);
+    }
+
+    public FileStat fstat(FileDescriptor fileDescriptor) {
+        FileStat stat = allocateStat();
+
+        if (fstat(fileDescriptor, stat) < 0) handler.error(Errno.valueOf(errno()), "fstat", "" + helper.getfd(fileDescriptor));
+        
+        return stat;
+    }
+
+    public FileStat fstat(int fd) {
+        FileStat stat = allocateStat();
+        if (fstat(fd, stat) < 0) handler.error(Errno.valueOf(errno()), "fstat", "" + fd);  
+        return stat;
+    }
+
+    
+    public int fstat(FileDescriptor fileDescriptor, FileStat stat) {
+        int fd = helper.getfd(fileDescriptor);
+        return libc().fstat(fd, stat);
+    }
+
+    public int fstat(int fd, FileStat stat) {
+        return libc().fstat(fd, stat);
+    }
+
+    public Pointer environ() {
+        return getRuntime().getMemoryManager().newPointer(libc().environ().get());
+    }
+
+    public String getenv(String envName) {
+        return libc().getenv(envName);
+    }
+
+    public int getegid() {
+        return libc().getegid();
+    }
+
+    public int geteuid() {
+        return libc().geteuid();
+    }
+
+    public int getgid() {
+        return libc().getgid();
+    }
+
+    public int getdtablesize() {
+        return libc().getdtablesize();
+    }
+
+    public String getlogin() {
+        return libc().getlogin();
+    }
+
+    public int getpgid() {
+        return libc().getpgid();
+    }
+
+    public int getpgrp() {
+        return libc().getpgrp();
+    }
+
+    public int getpid() {
+        return libc().getpid();
+    }
+
+    public int getppid() {
+        return libc().getppid();
+    }
+    
+    public Passwd getpwent() {
+        return libc().getpwent();
+    }
+
+    public Passwd getpwuid(int which) {
+        return libc().getpwuid(which);
+    }
+
+    public Passwd getpwnam(String which) {
+        return libc().getpwnam(which);
+    }
+
+    public Group getgrent() {
+        return libc().getgrent();
+    }
+    public Group getgrgid(int which) {
+        return libc().getgrgid(which);
+    }
+    public Group getgrnam(String which) {
+        return libc().getgrnam(which);
+    }
+
+    public int setpwent() {
+        return libc().setpwent();
+    }
+
+    public int endpwent() {
+        return libc().endpwent();
+    }
+
+    public int setgrent() {
+        return libc().setgrent();
+    }
+
+    public int endgrent() {
+        return libc().endgrent();
+    }
+
+    public int getuid() {
+        return libc().getuid();
+    }
+
+    public int getrlimit(int resource, RLimit rlim) {
+        return libc().getrlimit(resource, rlim);
+    }
+
+    public int getrlimit(int resource, Pointer rlim) {
+        return libc().getrlimit(resource, rlim);
+    }
+
+    public RLimit getrlimit(int resource) {
+        RLimit rlim = new DefaultNativeRLimit(getRuntime());
+
+        if (getrlimit(resource, rlim) < 0) handler.error(Errno.valueOf(errno()), "rlim");
+
+        return rlim;
+    }
+
+    public int setrlimit(int resource, RLimit rlim) {
+        return libc().setrlimit(resource, rlim);
+    }
+
+    public int setrlimit(int resource, Pointer rlim) {
+        return libc().setrlimit(resource, rlim);
+    }
+
+    public int setrlimit(int resource, long rlimCur, long rlimMax) {
+        RLimit rlim = new DefaultNativeRLimit(getRuntime());
+        rlim.init(rlimCur, rlimMax);
+
+        return libc().setrlimit(resource, rlim);
+    }
+
+    public int setegid(int egid) {
+        return libc().setegid(egid);
+    }
+
+    public int seteuid(int euid) {
+        return libc().seteuid(euid);
+    }
+
+    public int setgid(int gid) {
+        return libc().setgid(gid);
+    }
+    
+    public int getfd(FileDescriptor descriptor) {
+        return helper.getfd(descriptor);
+    }
+
+    public int getpgid(int pid) {
+        return libc().getpgid(pid);
+    }
+
+    public int setpgid(int pid, int pgid) {
+        return libc().setpgid(pid, pgid);
+    }
+
+    public int setpgrp(int pid, int pgrp) {
+        return libc().setpgrp(pid, pgrp);
+    }
+
+    public int setsid() {
+        return libc().setsid();
+    }
+
+    public int setuid(int uid) {
+        return libc().setuid(uid);
+    }
+
+    public int kill(int pid, int signal) {
+        return kill((long) pid, signal);
+    }
+
+    public int kill(long pid, int signal) {
+        return libc().kill(pid, signal);
+    }
+
+    public SignalHandler signal(Signal sig, final SignalHandler handler) {
+        synchronized (signalHandlers) {
+            SignalHandler old = signalHandlers.get(sig);
+
+            long result = libc().signal(sig.intValue(), new LibC.LibCSignalHandler() {
+                public void signal(int sig) {
+                    handler.handle(sig);
+                }
+            });
+
+            if (result != -1) {
+                signalHandlers.put(sig, handler);
+            }
+
+            return old;
+        }
+    }
+
+    public int raise(int sig) {
+        return libc().raise(sig);
+    }
+
+    public int lchmod(String filename, int mode) {
+        try {
+            return libc().lchmod(filename, mode);
+        } catch (UnsatisfiedLinkError ule) {
+            return unimplementedInt();
+        }
+    }
+
+    public int lchown(String filename, int user, int group) {
+        try {
+            return libc().lchown(filename, user, group);
+        } catch (UnsatisfiedLinkError ule) {
+            return unimplementedInt();
+        }
+    }
+
+    public int link(String oldpath, String newpath) {
+        return libc().link(oldpath, newpath);
+    }
+    
+    public FileStat lstat(String path) {
+        FileStat stat = allocateStat();
+        
+        if (lstat(path, stat) < 0) handler.error(Errno.valueOf(errno()), "lstat", path);
+        
+        return stat;
+    }
+    
+    public int lstat(String path, FileStat stat) {
+        return libc().lstat(path, stat);
+    }
+
+    public int mkdir(String path, int mode) {
+        int res = libc().mkdir(path, mode);
+        if (res < 0) {
+            int errno = errno();
+            handler.error(Errno.valueOf(errno), "mkdir", path);
+        }
+        return res;
+    }
+
+    public int rmdir(String path) {
+        int res = libc().rmdir(path);
+
+        if (res < 0) handler.error(Errno.valueOf(errno()), "rmdir", path);
+
+        return res;
+    }
+    
+    public int setenv(String envName, String envValue, int overwrite) {
+        return libc().setenv(envName, envValue, overwrite);
+    }
+
+    public FileStat stat(String path) {
+        FileStat stat = allocateStat();
+
+        if (stat(path, stat) < 0) handler.error(Errno.valueOf(errno()), "stat", path);
+        
+        return stat;
+    }
+
+    public int stat(String path, FileStat stat) {
+        return libc().stat(path, stat);
+    }
+
+    public int symlink(String oldpath, String newpath) {
+        return libc().symlink(oldpath, newpath);
+    }
+    
+    public String readlink(String oldpath) throws IOException {
+        // TODO: this should not be hardcoded to 1024 bytes
+        ByteBuffer buffer = ByteBuffer.allocate(1024);
+        int result = libc().readlink(oldpath, buffer, buffer.capacity());
+        
+        if (result == -1) return null;
+        
+        buffer.position(0);
+        buffer.limit(result);
+        return Charset.defaultCharset().decode(buffer).toString();
+    }
+
+    public int readlink(CharSequence path, byte[] buf, int bufsize) {
+        return libc().readlink(path, buf, bufsize);
+    }
+
+    public int readlink(CharSequence path, ByteBuffer buf, int bufsize) {
+        return libc().readlink(path, buf, bufsize);
+    }
+
+    public int readlink(CharSequence path, Pointer bufPtr, int bufsize) {
+        return libc().readlink(path, bufPtr, bufsize);
+    }
+
+    public int unsetenv(String envName) {
+        return libc().unsetenv(envName);
+    }
+    
+    public int umask(int mask) {
+        return libc().umask(mask);
+    }
+    
+    public int utimes(String path, long[] atimeval, long[] mtimeval) {
+        Timeval[] times = null;
+        if (atimeval != null && mtimeval != null) {
+            times = Struct.arrayOf(getRuntime(), DefaultNativeTimeval.class, 2);
+            times[0].setTime(atimeval);
+            times[1].setTime(mtimeval);
+        }
+        return libc().utimes(path, times);
+    }
+
+    public int utimes(String path, Pointer times) {
+        return libc().utimes(path, times);
+    }
+
+    public int futimes(int fd, long[] atimeval, long[] mtimeval) {
+        Timeval[] times = null;
+        if (atimeval != null && mtimeval != null) {
+            times = Struct.arrayOf(getRuntime(), DefaultNativeTimeval.class, 2);
+            times[0].setTime(atimeval);
+            times[1].setTime(mtimeval);
+        }
+        return libc().futimes(fd, times);
+    }
+
+    public int lutimes(String path, long[] atimeval, long[] mtimeval) {
+        Timeval[] times = null;
+        if (atimeval != null && mtimeval != null) {
+            times = Struct.arrayOf(getRuntime(), DefaultNativeTimeval.class, 2);
+            times[0].setTime(atimeval);
+            times[1].setTime(mtimeval);
+        }
+        return libc().lutimes(path, times);
+    }
+
+    public int utimensat(int dirfd, String path, long[] atimespec, long[] mtimespec, int flag) {
+        Timespec[] times = null;
+        if (atimespec != null && mtimespec != null) {
+            times = Struct.arrayOf(getRuntime(), DefaultNativeTimespec.class, 2);
+            times[0].setTime(atimespec);
+            times[1].setTime(mtimespec);
+        }
+        return libc().utimensat(dirfd, path, times, flag);
+    }
+
+    public int utimensat(int dirfd, String path, Pointer times, int flag) {
+        return libc().utimensat(dirfd, path, times, flag);
+    }
+
+    public int futimens(int fd, long[] atimespec, long[] mtimespec) {
+        Timespec[] times = null;
+        if (atimespec != null && mtimespec != null) {
+            times = Struct.arrayOf(getRuntime(), DefaultNativeTimespec.class, 2);
+            times[0].setTime(atimespec);
+            times[1].setTime(mtimespec);
+        }
+        return libc().futimens(fd, times);
+    }
+
+    public int futimens(int fd, Pointer times) {
+        return libc().futimens(fd, times);
+    }
+
+    public int fork() {
+        return libc().fork();
+    }
+    
+    public int waitpid(int pid, int[] status, int flags) {
+        return waitpid((long)pid, status, flags);
+    }
+
+    public int waitpid(long pid, int[] status, int flags) {
+        return libc().waitpid(pid, status, flags);
+    }
+    
+    public int wait(int[] status) {
+        return libc().wait(status);
+    }
+    
+    public int getpriority(int which, int who) {
+        return libc().getpriority(which, who);
+    }
+    
+    public int setpriority(int which, int who, int prio) {
+        return libc().setpriority(which, who, prio);
+    }
+
+    public boolean isatty(FileDescriptor fd) {
+       return isatty(helper.getfd(fd)) != 0;
+    }
+
+    public int isatty(int fd) {
+        return libc().isatty(fd);
+    }
+
+    public int errno() {
+        return LastError.getLastError(getRuntime());
+    }
+
+    public void errno(int value) {
+        LastError.setLastError(getRuntime(), value);
+    }
+    
+    public int chdir(String path) {
+        return libc().chdir(path);
+    }
+
+    public boolean isNative() {
+        return true;
+    }
+
+    public long posix_spawnp(String path, Collection<? extends SpawnFileAction> fileActions,
+                             CharSequence[] argv, CharSequence[] envp) {
+        return posix_spawnp(path, fileActions, null, argv, envp);
+    }
+
+    public long posix_spawnp(String path, Collection<? extends SpawnFileAction> fileActions,
+                            Collection<? extends CharSequence> argv, Collection<? extends CharSequence> envp) {
+        return posix_spawnp(path, fileActions, null, argv, envp);
+    }
+    
+    public long posix_spawnp(String path, Collection<? extends SpawnFileAction> fileActions,
+                             Collection<? extends SpawnAttribute> spawnAttributes,
+                             Collection<? extends CharSequence> argv, Collection<? extends CharSequence> envp) {
+        
+        CharSequence[] nativeArgv = new CharSequence[argv.size()];
+        argv.toArray(nativeArgv);
+
+        CharSequence[] nativeEnv = new CharSequence[envp.size()];
+        envp.toArray(nativeEnv);
+
+        return posix_spawnp(path, fileActions, spawnAttributes, nativeArgv, nativeEnv);
+    }
+
+    public long posix_spawnp(String path, Collection<? extends SpawnFileAction> fileActions,
+                             Collection<? extends SpawnAttribute> spawnAttributes,
+                             CharSequence[] argv, CharSequence[] envp) {
+//        AbstractNumberReference<? extends Number> pid = getRuntime().findType(TypeAlias.pid_t).size() == 4
+//            ? new IntByReference(-1) : new LongLongByReference(-1);
+        NumberByReference pid = new NumberByReference(TypeAlias.pid_t);
+        Pointer nativeFileActions = fileActions != null && !fileActions.isEmpty() ? nativeFileActions(fileActions) : null;
+        Pointer nativeSpawnAttributes = spawnAttributes != null && !spawnAttributes.isEmpty() ? nativeSpawnAttributes(spawnAttributes) : null;
+        long result;
+
+        try {
+            result = ((UnixLibC) libc()).posix_spawnp(pid, path, nativeFileActions, nativeSpawnAttributes, argv, envp);
+        } finally {
+            if (nativeFileActions != null) ((UnixLibC) libc()).posix_spawn_file_actions_destroy(nativeFileActions);
+            if (nativeSpawnAttributes != null) ((UnixLibC) libc()).posix_spawnattr_destroy(nativeSpawnAttributes);
+        }
+
+        if (result != 0) return -1; // result will be errno, but we can't indicate error because we return pid
+        return pid.longValue();
+    }
+    
+    public int flock(int fd, int mode) {
+        return libc().flock(fd, mode);
+    }
+
+    public int dup(int fd) {
+        return libc().dup(fd);
+    }
+
+    public int dup2(int oldFd, int newFd) {
+        return libc().dup2(oldFd, newFd);
+    }
+
+    public int fcntlInt(int fd, Fcntl fcntl, int arg) {
+        return fcntl(fd, fcntl, arg);
+    }
+
+    public int fcntl(int fd, Fcntl fcntl) {
+        return libc().fcntl(fd, fcntl.intValue());
+    }
+
+    public int fcntl(int fd, Fcntl fcntl, int arg) {
+        return libc().fcntl(fd, fcntl.intValue(), arg);
+    }
+
+    @Deprecated
+    public int fcntl(int fd, Fcntl fcntl, int... args) {
+        if (args != null) {
+            if (args.length == 1) {
+                return fcntl(fd, fcntl, args[0]);
+            }
+        }
+        throw new IllegalArgumentException("fcntl with variadic int args is unsupported");
+    }
+
+    public int access(CharSequence path, int amode) {
+        return libc().access(path, amode);
+    }
+
+    public int close(int fd) {
+        return libc().close(fd);
+    }
+
+    private Pointer nativeFileActions(Collection<? extends SpawnFileAction> fileActions) {
+        Pointer nativeFileActions = allocatePosixSpawnFileActions();
+        ((UnixLibC) libc()).posix_spawn_file_actions_init(nativeFileActions);
+        for (SpawnFileAction action : fileActions) {
+            action.act(this, nativeFileActions);
+        }
+
+        return nativeFileActions;
+    }
+
+    private Pointer nativeSpawnAttributes(Collection<? extends SpawnAttribute> spawnAttributes) {
+        Pointer nativeSpawnAttributes = allocatePosixSpawnattr();
+        ((UnixLibC) libc()).posix_spawnattr_init(nativeSpawnAttributes);
+        for (SpawnAttribute action : spawnAttributes) {
+            action.set(this, nativeSpawnAttributes);
+        }
+
+        return nativeSpawnAttributes;
+    }
+
+    public abstract FileStat allocateStat();
+
+    public long sysconf(Sysconf name) {
+        switch (name) {
+            case _SC_CLK_TCK:
+                return JavaTimes.HZ;
+
+            default:
+                errno(Errno.EOPNOTSUPP.intValue());
+                return -1;
+        }
+    }
+
+    public int confstr(Confstr name, ByteBuffer buf, int len) {
+        errno(Errno.EOPNOTSUPP.intValue());
+        return -1;
+    }
+
+    public int fpathconf(int fd, Pathconf name) {
+        errno(Errno.EOPNOTSUPP.intValue());
+        return -1;
+    }
+
+    public Times times() {
+        return new JavaTimes();
+    }
+
+    public int unlink(CharSequence path) {
+        return libc().unlink(path);
+    }
+
+    public int open(CharSequence path, int flags, int perm) {
+        return libc().open(path, flags, perm);
+    }
+
+    public long read(int fd, byte[] buf, long n) {
+        return libc().read(fd, buf, n);
+    }
+    public long write(int fd, byte[] buf, long n) {
+        return libc().write(fd, buf, n);
+    }
+    public long read(int fd, ByteBuffer buf, long n) {
+        return libc().read(fd, buf, n);
+    }
+    public long write(int fd, ByteBuffer buf, long n) {
+        return libc().write(fd, buf, n);
+    }
+    public long pread(int fd, byte[] buf, long n, long offset) {
+        return libc().pread(fd, buf, n, offset);
+    }
+    public long pwrite(int fd, byte[] buf, long n, long offset) {
+        return libc().pwrite(fd, buf, n, offset);
+    }
+    public long pread(int fd, ByteBuffer buf, long n, long offset) {
+        return libc().pread(fd, buf, n, offset);
+    }
+    public long pwrite(int fd, ByteBuffer buf, long n, long offset) {
+        return libc().pwrite(fd, buf, n, offset);
+    }
+
+    public int read(int fd, byte[] buf, int n) {
+        return libc().read(fd, buf, n);
+    }
+    public int write(int fd, byte[] buf, int n) {
+        return libc().write(fd, buf, n);
+    }
+    public int read(int fd, ByteBuffer buf, int n) {
+        return libc().read(fd, buf, n);
+    }
+    public int write(int fd, ByteBuffer buf, int n) {
+        return libc().write(fd, buf, n);
+    }
+    public int pread(int fd, byte[] buf, int n, int offset) {
+        return libc().pread(fd, buf, n, offset);
+    }
+    public int pwrite(int fd, byte[] buf, int n, int offset) {
+        return libc().pwrite(fd, buf, n, offset);
+    }
+    public int pread(int fd, ByteBuffer buf, int n, int offset) {
+        return libc().pread(fd, buf, n, offset);
+    }
+    public int pwrite(int fd, ByteBuffer buf, int n, int offset) {
+        return libc().pwrite(fd, buf, n, offset);
+    }
+
+    public int lseek(int fd, long offset, int whence) {
+        return (int) libc().lseek(fd, offset, whence);
+    }
+
+    public long lseekLong(int fd, long offset, int whence) {
+        return libc().lseek(fd, offset, whence);
+    }
+
+    public int pipe(int[] fds) {
+        return libc().pipe(fds);
+    }
+
+    public int socketpair(int domain, int type, int protocol, int[] fds) {
+        return libc().socketpair(domain, type, protocol, fds);
+    }
+
+    public int sendmsg(int socket, MsgHdr message, int flags) {
+        return libc().sendmsg( socket, message, flags );
+    }
+
+    public int recvmsg(int socket, MsgHdr message, int flags) {
+        return libc().recvmsg(socket, message, flags);
+    }
+
+    public int truncate(CharSequence path, long length) {
+        return libc().truncate(path, length);
+    }
+
+    public int ftruncate(int fd, long offset) {
+        return libc().ftruncate(fd, offset);
+    }
+
+    public int rename(CharSequence oldName, CharSequence newName) {
+        return libc().rename(oldName, newName);
+    }
+
+    public String gethostname() {
+        ByteBuffer buffer = ByteBuffer.allocate(256);
+        int result;
+        try {
+            result = libc().gethostname(buffer, buffer.capacity() - 1);
+        } catch (java.lang.UnsatisfiedLinkError e) {
+            result = -1;
+        }
+        if (result == -1) return helper.gethostname();
+        buffer.position(0);
+        while (buffer.hasRemaining() && buffer.get() != 0);
+        buffer.limit(buffer.position() - 1);
+        buffer.position(0);
+        return Charset.forName("US-ASCII").decode(buffer).toString();
+    }
+
+    public String getcwd() {
+        byte[] cwd = new byte[1024];
+        long result = libc().getcwd(cwd, 1024);
+        if (result == -1) return null;
+        int len = 0;
+        for (; len < 1024; len++) if (cwd[len] == 0) break;
+        return new String(cwd, 0, len);
+    }
+
+    public int fsync(int fd) {
+        return libc().fsync(fd);
+    }
+
+    public int fdatasync(int fd) {
+        return libc().fdatasync(fd);
+    }
+
+    public static abstract class PointerConverter implements FromNativeConverter {
+        public Class nativeType() {
+            return Pointer.class;
+        }
+    }
+    
+    public static final PointerConverter GROUP = new PointerConverter() {
+        public Object fromNative(Object arg, FromNativeContext ctx) {
+            return arg != null ? new DefaultNativeGroup((Pointer) arg) : null;
+        }
+    };
+
+    public static final ToNativeConverter<FileStat, Pointer> FileStatConverter = new ToNativeConverter<FileStat, Pointer>() {
+
+        public Pointer toNative(FileStat value, ToNativeContext context) {
+            if (value instanceof BaseFileStat) {
+                return ((BaseFileStat) value).memory;
+
+            } else if (value instanceof Struct) {
+                return Struct.getMemory((Struct) value);
+
+            } else if (value == null) {
+                return null;
+            }
+
+            throw new IllegalArgumentException("instance of " + value.getClass() + " is not a struct");
+        }
+
+        public Class<Pointer> nativeType() {
+            return Pointer.class;
+        }
+
+    };
+
+    public static final ToNativeConverter<NativeTimes, Pointer> TimesConverter = new ToNativeConverter<NativeTimes, Pointer>() {
+
+        public Pointer toNative(NativeTimes value, ToNativeContext context) {
+            return value.memory;
+        }
+
+        public Class<Pointer> nativeType() {
+            return Pointer.class;
+        }
+    };
+
+    public static final ToNativeConverter<Constant, Integer> ConstantConverter = new ToNativeConverter<Constant, Integer>() {
+
+        public Integer toNative(Constant value, ToNativeContext context) {
+            return value.intValue();
+        }
+
+        public Class<Integer> nativeType() {
+            return Integer.class;
+        }
+    };
+
+    public static final ToNativeConverter<MsgHdr, Pointer> MsgHdrConverter = new ToNativeConverter<MsgHdr, Pointer>() {
+        public Pointer toNative(MsgHdr value, ToNativeContext context) {
+            if ( value instanceof BaseMsgHdr ) {
+                return ((BaseMsgHdr) value).memory;
+            } else if ( value instanceof Struct ) {
+                return Struct.getMemory((Struct) value);
+            } else if ( value == null ) {
+                return null;
+            }
+
+            throw new IllegalArgumentException("instance of " + value.getClass() + " is not a struct");
+        }
+
+        public Class<Pointer> nativeType() {
+            return Pointer.class;
+        }
+    };
+
+    public int mkfifo(String filename, int mode) {
+        return ((UnixLibC) libc()).mkfifo(filename, mode);
+    }
+
+    public int daemon(int nochdir, int noclose) {
+        return libc().daemon(nochdir, noclose);
+    }
+
+    @Override
+    public long[] getgroups() {
+        final int size = getgroups(0, null);
+        final int[] groups = new int[size];
+        final long[] castGroups = new long[size];
+
+        final int actualSize = getgroups(size, groups);
+
+        if (actualSize == -1) {
+            return null;
+        }
+
+        for (int i = 0; i < actualSize; i++) {
+            castGroups[i] = groups[i] & 0xFFFFFFFFL;
+        }
+
+        if (actualSize < size) {
+            return Arrays.copyOfRange(castGroups, 0, actualSize);
+        }
+
+        return castGroups;
+    }
+
+    @Override
+    public int getgroups(int size, int[] groups) {
+        return libc().getgroups(size, groups);
+    }
+
+    @Override
+    public String nl_langinfo(int item) {
+        return libc().nl_langinfo(item);
+    }
+
+    @Override
+    public String setlocale(int category, String locale) {
+        return libc().setlocale(category, locale);
+    }
+
+    @Override
+    public String strerror(int code) {
+        return libc().strerror(code);
+    }
+
+    @Override
+    public Timeval allocateTimeval() { return new DefaultNativeTimeval(getRuntime()); }
+
+    @Override
+    public int gettimeofday(Timeval tv) { return libc().gettimeofday(tv, 0); }
+}
diff --git a/src/main/java/jnr/posix/CheckedPOSIX.java b/src/main/java/jnr/posix/CheckedPOSIX.java
new file mode 100644
index 0000000..887929d
--- /dev/null
+++ b/src/main/java/jnr/posix/CheckedPOSIX.java
@@ -0,0 +1,644 @@
+package jnr.posix;
+
+import jnr.constants.platform.Fcntl;
+import jnr.constants.platform.Signal;
+import jnr.constants.platform.Sysconf;
+import jnr.ffi.Pointer;
+import jnr.posix.util.MethodName;
+import jnr.posix.util.ProcessMaker;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Collection;
+import jnr.constants.platform.Confstr;
+import jnr.constants.platform.Pathconf;
+
+final class CheckedPOSIX implements POSIX {
+    private final POSIX posix;
+    private final POSIXHandler handler;
+    
+    CheckedPOSIX(POSIX posix, POSIXHandler handler) {
+        this.posix = posix;
+        this.handler = handler;
+    }
+
+    private <T> T unimplementedNull() {
+        handler.unimplementedError(MethodName.getCallerMethodName());
+        return null;
+    }
+
+    private int unimplementedInt() {
+        handler.unimplementedError(MethodName.getCallerMethodName());
+        return -1;
+    }
+
+    private boolean unimplementedBool() {
+        handler.unimplementedError(MethodName.getCallerMethodName());
+        return false;
+    }
+
+    private String unimplementedString() {
+        handler.unimplementedError(MethodName.getCallerMethodName());
+        return null;
+    }
+
+    public ProcessMaker newProcessMaker(String... command) {
+        try { return posix.newProcessMaker(command); } catch (UnsatisfiedLinkError ule) { return unimplementedNull(); }
+    }
+
+    public ProcessMaker newProcessMaker() {
+        try { return posix.newProcessMaker(); } catch (UnsatisfiedLinkError ule) { return unimplementedNull(); }
+    }
+
+    public FileStat allocateStat() {
+        try { return posix.allocateStat(); } catch (UnsatisfiedLinkError ule) { return unimplementedNull(); }
+    }
+
+    public MsgHdr allocateMsgHdr() {
+        try { return posix.allocateMsgHdr(); } catch (UnsatisfiedLinkError ule) { return unimplementedNull(); }
+    }
+
+    public int chdir(String path) {
+        try { return posix.chdir(path); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int chmod(String filename, int mode) {
+        try { return posix.chmod(filename, mode); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int fchmod(int fd, int mode) {
+        try { return posix.fchmod(fd, mode); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int chown(String filename, int user, int group) {
+        try { return posix.chown(filename, user, group); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public CharSequence crypt(CharSequence key, CharSequence salt) {
+        try { return posix.crypt(key, salt); } catch (UnsatisfiedLinkError ule) { return unimplementedNull(); }
+    }
+
+    public byte[] crypt(byte[] key, byte[] salt) {
+        try { return posix.crypt(key, salt); } catch (UnsatisfiedLinkError ule) { return unimplementedNull(); }
+    }
+
+    public int fchown(int fd, int user, int group) {
+        try { return posix.fchown(fd, user, group); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int endgrent() {
+        try { return posix.endgrent(); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int endpwent() {
+        try { return posix.endpwent(); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int errno() {
+        return posix.errno();
+    }
+
+    public void errno(int value) {
+        posix.errno(value);
+    }
+
+    public int exec(String path, String... args) {
+        try { return posix.exec(path, args); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int exec(String path, String[] args, String[] envp) {
+        try { return posix.exec(path, args, envp); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int execv(String path, String[] argv) {
+        try { return posix.execv(path, argv); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int execve(String path, String[] argv, String[] envp) {
+        try { return posix.execve(path, argv, envp); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int fork() {
+        try { return posix.fork(); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public FileStat fstat(int fd) {
+        try { return posix.fstat(fd); } catch (UnsatisfiedLinkError ule) { return unimplementedNull(); }
+    }
+
+    public int fstat(int fd, FileStat stat) {
+        try { return posix.fstat(fd, stat); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public FileStat fstat(FileDescriptor descriptor) {
+        try { return posix.fstat(descriptor); } catch (UnsatisfiedLinkError ule) { return unimplementedNull(); }
+    }
+
+    public int fstat(FileDescriptor descriptor, FileStat stat) {
+        try { return posix.fstat(descriptor, stat); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int getegid() {
+        try { return posix.getegid(); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int geteuid() {
+        try { return posix.geteuid(); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int getgid() {
+        try { return posix.getgid(); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int getdtablesize() {
+        try { return posix.getdtablesize(); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public Group getgrent() {
+        try { return posix.getgrent(); } catch (UnsatisfiedLinkError ule) { return unimplementedNull(); }
+    }
+
+    public Group getgrgid(int which) {
+        try { return posix.getgrgid(which); } catch (UnsatisfiedLinkError ule) { return unimplementedNull(); }
+    }
+
+    public Group getgrnam(String which) {
+        try { return posix.getgrnam(which); } catch (UnsatisfiedLinkError ule) { return unimplementedNull(); }
+    }
+
+    public String getlogin() {
+        try { return posix.getlogin(); } catch (UnsatisfiedLinkError ule) { return unimplementedNull(); }
+    }
+
+    public int getpgid() {
+        try { return posix.getpgid(); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int getpgid(int pid) {
+        try { return posix.getpgid(pid); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int getpgrp() {
+        try { return posix.getpgrp(); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int getpid() {
+        try { return posix.getpid(); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int getppid() {
+        try { return posix.getppid(); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int getpriority(int which, int who) {
+        try { return posix.getpriority(which, who); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public Passwd getpwent() {
+        try { return posix.getpwent(); } catch (UnsatisfiedLinkError ule) { return unimplementedNull(); }
+    }
+
+    public Passwd getpwnam(String which) {
+        try { return posix.getpwnam(which); } catch (UnsatisfiedLinkError ule) { return unimplementedNull(); }
+    }
+
+    public Passwd getpwuid(int which) {
+        try { return posix.getpwuid(which); } catch (UnsatisfiedLinkError ule) { return unimplementedNull(); }
+    }
+
+    public int getuid() {
+        try { return posix.getuid(); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int getrlimit(int resource, RLimit rlim) {
+        try { return posix.getrlimit(resource, rlim); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int getrlimit(int resource, Pointer rlim) {
+        try { return posix.getrlimit(resource, rlim); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public RLimit getrlimit(int resource) {
+        try { return posix.getrlimit(resource); } catch (UnsatisfiedLinkError ule) { return unimplementedNull(); }
+    }
+
+    public int setrlimit(int resource, RLimit rlim) {
+        try { return posix.setrlimit(resource, rlim); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int setrlimit(int resource, Pointer rlim) {
+        try { return posix.setrlimit(resource, rlim); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int setrlimit(int resource, long rlimCur, long rlimMax) {
+        try { return posix.setrlimit(resource, rlimCur, rlimMax); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public boolean isatty(FileDescriptor descriptor) {
+        try { return posix.isatty(descriptor); } catch (UnsatisfiedLinkError ule) { return unimplementedBool(); }
+    }
+
+    public int isatty(int descriptor) {
+        try { return posix.isatty(descriptor); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int kill(int pid, int signal) {
+        return kill((long) pid, signal);
+    }
+
+    public int kill(long pid, int signal) {
+        try { return posix.kill(pid, signal); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public SignalHandler signal(Signal sig, SignalHandler handler) {
+        try { return posix.signal(sig, handler); } catch (UnsatisfiedLinkError ule) { return unimplementedNull(); }
+    }
+
+    public int raise(int sig) {
+        try { return posix.raise(sig); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int lchmod(String filename, int mode) {
+        try { return posix.lchmod(filename, mode); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int lchown(String filename, int user, int group) {
+        try { return posix.lchown(filename, user, group); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int link(String oldpath, String newpath) {
+        try { return posix.link(oldpath, newpath); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public FileStat lstat(String path) {
+        try { return posix.lstat(path); } catch (UnsatisfiedLinkError ule) { return unimplementedNull(); }
+    }
+
+    public int lstat(String path, FileStat stat) {
+        try { return posix.lstat(path, stat); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int mkdir(String path, int mode) {
+        try { return posix.mkdir(path, mode); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public String readlink(String path) throws IOException {
+        try { return posix.readlink(path); } catch (UnsatisfiedLinkError ule) { return unimplementedNull(); }
+    }
+
+    public int readlink(CharSequence path, byte[] buf, int bufsize) {
+        try { return posix.readlink(path, buf, bufsize); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int readlink(CharSequence path, ByteBuffer buf, int bufsize) {
+        try { return posix.readlink(path, buf, bufsize); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int readlink(CharSequence path, Pointer bufPtr, int bufsize) {
+        try { return posix.readlink(path, bufPtr, bufsize); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int rmdir(String path) {
+        try { return posix.rmdir(path); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int setegid(int egid) {
+        try { return posix.setegid(egid); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int seteuid(int euid) {
+        try { return posix.seteuid(euid); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int setgid(int gid) {
+        try { return posix.setgid(gid); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int setgrent() {
+        try { return posix.setgrent(); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int setpgid(int pid, int pgid) {
+        try { return posix.setpgid(pid, pgid); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int setpgrp(int pid, int pgrp) {
+        try { return posix.setpgrp(pid, pgrp); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int setpriority(int which, int who, int prio) {
+        try { return posix.setpriority(which, who, prio); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int setpwent() {
+        try { return posix.setpwent(); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int setsid() {
+        try { return posix.setsid(); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int setuid(int uid) {
+        try { return posix.setuid(uid); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public FileStat stat(String path) {
+        try { return posix.stat(path); } catch (UnsatisfiedLinkError ule) { return unimplementedNull(); }
+    }
+
+    public int stat(String path, FileStat stat) {
+        try { return posix.stat(path, stat); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int symlink(String oldpath, String newpath) {
+        try { return posix.symlink(oldpath, newpath); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int umask(int mask) {
+        try { return posix.umask(mask); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int utimes(String path, long[] atimeval, long[] mtimeval) {
+        try { return posix.utimes(path, atimeval, mtimeval); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int utimes(String path, Pointer times) {
+        try { return posix.utimes(path, times); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int futimes(int fd, long[] atimeval, long[] mtimeval) {
+        try { return posix.futimes(fd, atimeval, mtimeval); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int lutimes(String path, long[] atimeval, long[] mtimeval) {
+        try { return posix.lutimes(path, atimeval, mtimeval); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int utimensat(int dirfd, String path, long[] atimespec, long[] mtimespec, int flag) {
+        try { return posix.utimensat(dirfd, path, atimespec, mtimespec, flag); } catch (UnsatisfiedLinkError ule) {
+            return unimplementedInt(); }
+    }
+
+    public int utimensat(int dirfd, String path, Pointer times, int flag) {
+        try { return posix.utimensat(dirfd, path, times, flag); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int futimens(int fd, long[] atimespec, long[] mtimespec) {
+        try { return posix.futimens(fd, atimespec, mtimespec); } catch (UnsatisfiedLinkError ule) {
+            return unimplementedInt(); }
+    }
+
+    public int futimens(int fd, Pointer times) {
+        try { return posix.futimens(fd, times); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int wait(int[] status) {
+        try { return posix.wait(status); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int waitpid(int pid, int[] status, int flags) {
+        return waitpid((long)pid, status, flags);
+    }
+
+    public int waitpid(long pid, int[] status, int flags) {
+        try { return posix.waitpid(pid, status, flags); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public boolean isNative() {
+        return posix.isNative();
+    }
+
+    public LibC libc() {
+        return posix.libc();
+    }
+
+    public Pointer environ() {
+        try { return posix.environ(); } catch (UnsatisfiedLinkError ule) { return unimplementedNull(); }
+    }
+
+    public String getenv(String envName) {
+        try { return posix.getenv(envName); } catch (UnsatisfiedLinkError ule) { return unimplementedNull(); }
+    }
+
+    public int setenv(String envName, String envValue, int overwrite) {
+        try { return posix.setenv(envName, envValue, overwrite); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int unsetenv(String envName) {
+        try { return posix.unsetenv(envName); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public long posix_spawnp(String path, Collection<? extends SpawnFileAction> fileActions, Collection<? extends CharSequence> argv, Collection<? extends CharSequence> envp) {
+        try { return posix.posix_spawnp(path, fileActions, argv, envp); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public long posix_spawnp(String path, Collection<? extends SpawnFileAction> fileActions,
+                             Collection<? extends SpawnAttribute> spawnAttributes,
+                             Collection<? extends CharSequence> argv, Collection<? extends CharSequence> envp) {
+        try { return posix.posix_spawnp(path, fileActions, spawnAttributes, argv, envp); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+
+    public long sysconf(Sysconf name) {
+        try { return posix.sysconf(name); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int confstr(Confstr name, ByteBuffer buf, int len) {
+        try { return posix.confstr(name, buf, len); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int fpathconf(int fd, Pathconf name) {
+        try { return posix.fpathconf(fd, name); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public Times times() {
+        try { return posix.times(); } catch (UnsatisfiedLinkError ule) { return unimplementedNull(); }
+    }
+    
+    public int flock(int fd, int mode) {
+        return posix.flock(fd, mode);
+    }
+
+    public int dup(int fd) {
+        try { return posix.dup(fd); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int dup2(int oldFd, int newFd) {
+        try { return posix.dup2(oldFd, newFd); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int fcntlInt(int fd, Fcntl fcntlConst, int arg) {
+        try { return posix.fcntlInt(fd, fcntlConst, arg); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int fcntl(int fd, Fcntl fcntlConst) {
+        try { return posix.fcntl(fd, fcntlConst); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int fcntl(int fd, Fcntl fcntlConst, int arg) {
+        try { return posix.fcntl(fd, fcntlConst, arg); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    @Deprecated
+    public int fcntl(int fd, Fcntl fcntlConst, int... arg) {
+        try { return posix.fcntl(fd, fcntlConst); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int access(CharSequence path, int amode) {
+        try { return posix.access(path, amode); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int close(int fd) {
+        try { return posix.close(fd); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int unlink(CharSequence path) {
+        try { return posix.unlink(path); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int open(CharSequence path, int flags, int perm) {
+        try { return posix.open(path, flags, perm); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public long read(int fd, byte[] buf, long n) {
+        try { return posix.read(fd, buf, n); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+    public long write(int fd, byte[] buf, long n) {
+        try { return posix.write(fd, buf, n); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+    public long read(int fd, ByteBuffer buf, long n) {
+        try { return posix.read(fd, buf, n); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+    public long write(int fd, ByteBuffer buf, long n) {
+        try { return posix.write(fd, buf, n); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+    public long pread(int fd, byte[] buf, long n, long offset) {
+        try { return posix.pread(fd, buf, n, offset); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+    public long pwrite(int fd, byte[] buf, long n, long offset) {
+        try { return posix.pwrite(fd, buf, n, offset); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+    public long pread(int fd, ByteBuffer buf, long n, long offset) {
+        try { return posix.pread(fd, buf, n, offset); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+    public long pwrite(int fd, ByteBuffer buf, long n, long offset) {
+        try { return posix.pwrite(fd, buf, n, offset); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int read(int fd, byte[] buf, int n) {
+        try { return posix.read(fd, buf, n); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+    public int write(int fd, byte[] buf, int n) {
+        try { return posix.write(fd, buf, n); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+    public int read(int fd, ByteBuffer buf, int n) {
+        try { return posix.read(fd, buf, n); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+    public int write(int fd, ByteBuffer buf, int n) {
+        try { return posix.write(fd, buf, n); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+    public int pread(int fd, byte[] buf, int n, int offset) {
+        try { return posix.pread(fd, buf, n, offset); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+    public int pwrite(int fd, byte[] buf, int n, int offset) {
+        try { return posix.pwrite(fd, buf, n, offset); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+    public int pread(int fd, ByteBuffer buf, int n, int offset) {
+        try { return posix.pread(fd, buf, n, offset); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+    public int pwrite(int fd, ByteBuffer buf, int n, int offset) {
+        try { return posix.pwrite(fd, buf, n, offset); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int lseek(int fd, long offset, int whence) {
+        try { return posix.lseek(fd, offset, whence); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public long lseekLong(int fd, long offset, int whence) {
+        try { return posix.lseekLong(fd, offset, whence); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int pipe(int[] fds) {
+        try {return posix.pipe(fds); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int socketpair(int domain, int type, int protocol, int[] fds) {
+        try {return posix.socketpair(domain, type, protocol, fds); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int sendmsg(int socket, MsgHdr message, int flags) {
+        try {return posix.sendmsg(socket, message, flags); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int recvmsg(int socket, MsgHdr message, int flags) {
+        try {return posix.recvmsg(socket, message, flags); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int truncate(CharSequence path, long length) {
+        try { return posix.truncate(path, length); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int ftruncate(int fd, long offset) {
+        try {return posix.ftruncate(fd, offset); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int rename(CharSequence oldName, CharSequence newName) {
+        try {return posix.rename(oldName, newName); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public String getcwd() {
+        try {return posix.getcwd(); } catch (UnsatisfiedLinkError ule) { return unimplementedString(); }
+    }
+
+    public int fsync(int fd) {
+        try {return posix.fsync(fd); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int fdatasync(int fd) {
+        try {return posix.fsync(fd); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int mkfifo(String path, int mode) {
+        try {return posix.mkfifo(path, mode); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public int daemon(int nochdir, int noclose) {
+        try {return posix.daemon(nochdir, noclose); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public long[] getgroups() {
+        try {return posix.getgroups(); } catch (UnsatisfiedLinkError ule) { return null; }
+    }
+
+    public int getgroups(int size, int[] groups) {
+        try {return posix.getgroups(size, groups); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public String nl_langinfo(int item) {
+        try {return posix.nl_langinfo(item); } catch (UnsatisfiedLinkError ule) { return unimplementedString(); }
+    }
+
+    public String setlocale(int category, String locale) {
+        try {return posix.setlocale(category, locale); } catch (UnsatisfiedLinkError ule) { return unimplementedString(); }
+    }
+
+    @Override
+    public String strerror(int code) {
+        try {return posix.strerror(code); } catch (UnsatisfiedLinkError ule) { return unimplementedString(); }
+    }
+
+    @Override
+    public Timeval allocateTimeval() {
+        try {return posix.allocateTimeval(); } catch (UnsatisfiedLinkError ule) { return unimplementedNull(); }
+    }
+
+    @Override
+    public int gettimeofday(Timeval tv) {
+        try {return posix.gettimeofday(tv); } catch (UnsatisfiedLinkError ule) { return unimplementedInt(); }
+    }
+
+    public String gethostname() {
+        try {return posix.gethostname(); } catch (UnsatisfiedLinkError ule) { return unimplementedNull(); }
+    }
+}
diff --git a/src/main/java/jnr/posix/CmsgHdr.java b/src/main/java/jnr/posix/CmsgHdr.java
new file mode 100644
index 0000000..a20fab8
--- /dev/null
+++ b/src/main/java/jnr/posix/CmsgHdr.java
@@ -0,0 +1,24 @@
+package jnr.posix;
+
+import java.nio.ByteBuffer;
+
+/**
+ * @author Bob McWhirter
+ */
+public interface CmsgHdr {
+
+    void setLevel(int level);
+
+    int getLevel();
+
+    void setType(int type);
+
+    int getType();
+
+    void setData(ByteBuffer data);
+
+    ByteBuffer getData();
+
+    int getLen();
+
+}
diff --git a/src/main/java/jnr/posix/Crypt.java b/src/main/java/jnr/posix/Crypt.java
new file mode 100644
index 0000000..34ef981
--- /dev/null
+++ b/src/main/java/jnr/posix/Crypt.java
@@ -0,0 +1,8 @@
+package jnr.posix;
+
+import jnr.ffi.Pointer;
+
+public interface Crypt {
+    CharSequence crypt(CharSequence key, CharSequence salt);
+    Pointer crypt(byte[] key, byte[] salt);
+}
diff --git a/src/main/java/jnr/posix/DefaultNativeGroup.java b/src/main/java/jnr/posix/DefaultNativeGroup.java
new file mode 100644
index 0000000..8bc6914
--- /dev/null
+++ b/src/main/java/jnr/posix/DefaultNativeGroup.java
@@ -0,0 +1,86 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: EPL 2.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Eclipse Public
+ * License Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+
+package jnr.posix;
+
+import jnr.ffi.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The default native group layout.
+ * 
+ * <p>
+ * This implementation should work on Solaris, Linux and MacOS.
+ * </p>
+ */
+public final class DefaultNativeGroup extends NativeGroup implements Group {
+    static final class Layout extends StructLayout {
+        public Layout(jnr.ffi.Runtime runtime) {
+            super(runtime);
+        }
+
+        public final UTF8StringRef gr_name = new UTF8StringRef();   // name
+        public final UTF8StringRef gr_passwd = new UTF8StringRef(); // group password (encrypted)
+        public final Signed32 gr_gid = new Signed32();       // group id
+        public final Pointer gr_mem = new Pointer();
+    }
+
+    static final Layout layout = new Layout(jnr.ffi.Runtime.getSystemRuntime());
+    private final Pointer memory;
+
+    DefaultNativeGroup(jnr.ffi.Pointer memory) {
+        super(memory.getRuntime(), layout);
+        this.memory = memory;
+    }
+
+    public java.lang.String getName() {
+        return layout.gr_name.get(memory);
+    }
+
+    public java.lang.String getPassword() {
+        return layout.gr_passwd.get(memory);
+    }
+
+    public long getGID() {
+        return layout.gr_gid.get(memory);
+    }
+
+    public java.lang.String[] getMembers() {
+        List<java.lang.String> lst = new ArrayList<java.lang.String>();
+
+        jnr.ffi.Pointer ptr = layout.gr_mem.get(memory);
+        Pointer member;
+        int ptrSize = runtime.addressSize();
+        for (int i = 0; (member = ptr.getPointer(i)) != null; i += ptrSize) {
+            lst.add(member.getString(0));
+        }
+
+        return lst.toArray(new java.lang.String[lst.size()]);
+    }
+
+}
diff --git a/src/main/java/jnr/posix/DefaultNativeRLimit.java b/src/main/java/jnr/posix/DefaultNativeRLimit.java
new file mode 100644
index 0000000..f285572
--- /dev/null
+++ b/src/main/java/jnr/posix/DefaultNativeRLimit.java
@@ -0,0 +1,26 @@
+package jnr.posix;
+
+public class DefaultNativeRLimit extends RLimit {
+    public final UnsignedLong rlim_cur = new UnsignedLong();
+    public final UnsignedLong rlim_max = new UnsignedLong();
+
+    protected DefaultNativeRLimit(jnr.ffi.Runtime runtime) {
+        super(runtime);
+    }
+
+    @Override
+    public void init(long rlimCur, long rlimMax) {
+        rlim_cur.set(rlimCur);
+        rlim_max.set(rlimMax);
+    }
+
+    @Override
+    public long rlimCur() {
+        return rlim_cur.get();
+    }
+
+    @Override
+    public long rlimMax() {
+        return rlim_max.get();
+    }
+}
diff --git a/src/main/java/jnr/posix/DefaultNativeTimespec.java b/src/main/java/jnr/posix/DefaultNativeTimespec.java
new file mode 100644
index 0000000..dda3b77
--- /dev/null
+++ b/src/main/java/jnr/posix/DefaultNativeTimespec.java
@@ -0,0 +1,32 @@
+package jnr.posix;
+
+public final class DefaultNativeTimespec extends Timespec {
+    public final SignedLong tv_sec = new SignedLong();
+    public final SignedLong tv_nsec = new SignedLong();
+
+    public DefaultNativeTimespec(jnr.ffi.Runtime runtime) {
+        super(runtime);
+    }
+
+    public void setTime(long[] timespec) {
+        assert timespec.length == 2;
+        tv_sec.set(timespec[0]);
+        tv_nsec.set(timespec[1]);
+    }
+
+    public void sec(long sec) {
+        this.tv_sec.set(sec);
+    }
+
+    public void nsec(long usec) {
+        this.tv_nsec.set(usec);
+    }
+
+    public long sec() {
+        return tv_sec.get();
+    }
+
+    public long nsec() {
+        return tv_nsec.get();
+    }
+}
diff --git a/src/main/java/jnr/posix/DefaultNativeTimeval.java b/src/main/java/jnr/posix/DefaultNativeTimeval.java
new file mode 100644
index 0000000..0cb2aa6
--- /dev/null
+++ b/src/main/java/jnr/posix/DefaultNativeTimeval.java
@@ -0,0 +1,32 @@
+package jnr.posix;
+
+public final class DefaultNativeTimeval extends Timeval {
+    public final SignedLong tv_sec = new SignedLong();
+    public final SignedLong tv_usec = new SignedLong();
+
+    public DefaultNativeTimeval(jnr.ffi.Runtime runtime) {
+        super(runtime);
+    }
+
+    public void setTime(long[] timeval) {
+        assert timeval.length == 2;
+        tv_sec.set(timeval[0]);
+        tv_usec.set(timeval[1]);
+    }
+
+    public void sec(long sec) {
+        this.tv_sec.set(sec);
+    }
+
+    public void usec(long usec) {
+        this.tv_usec.set(usec);
+    }
+
+    public long sec() {
+        return tv_sec.get();
+    }
+
+    public long usec() {
+        return tv_usec.get();
+    }
+}
diff --git a/src/main/java/jnr/posix/DragonFlyFileStat.java b/src/main/java/jnr/posix/DragonFlyFileStat.java
new file mode 100644
index 0000000..89dba35
--- /dev/null
+++ b/src/main/java/jnr/posix/DragonFlyFileStat.java
@@ -0,0 +1,141 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: EPL 2.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Eclipse Public
+ * License Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+/**
+ * $Id: $
+ */
+
+package jnr.posix;
+
+import jnr.ffi.StructLayout;
+
+public final class DragonFlyFileStat extends BaseFileStat implements NanosecondFileStat {
+    private static final class Layout extends StructLayout {
+
+        private Layout(jnr.ffi.Runtime runtime) {
+            super(runtime);
+        }
+
+        public final class time_t extends SignedLong {}
+        public final class dev_t extends Unsigned32 {}
+
+        public final Signed64    st_ino = new Signed64();
+        public final Signed32    st_nlink = new Signed32();
+        public final dev_t       st_dev = new dev_t();
+        public final Unsigned16  st_mode = new Unsigned16();
+        public final Unsigned16  st_padding1 = new Unsigned16();
+        public final Signed32    st_uid = new Signed32();
+        public final Signed32    st_gid = new Signed32();
+        public final dev_t       st_rdev = new dev_t();
+        public final time_t      st_atim = new time_t();
+        public final time_t      st_atimnsec = new time_t();
+        public final time_t      st_mtim = new time_t();
+        public final time_t      st_mtimnsec = new time_t();
+        public final time_t      st_ctim = new time_t();
+        public final time_t      st_ctimnsec = new time_t();
+        public final Signed32    st_size = new Signed32();
+        public final Signed32    st_blocks = new Signed32();
+        public final Signed32    st_blksize = new Signed32();
+        public final Signed32    st_flags = new Signed32();
+        public final Signed32    st_gen = new Signed32();
+        public final Signed32    st_lspare = new Signed32();
+        public final Signed64    st_qspare1 = new Signed64();
+        public final Signed64    st_qspare2 = new Signed64();
+    }
+    private static final Layout layout = new Layout(jnr.ffi.Runtime.getSystemRuntime());
+
+    public DragonFlyFileStat(NativePOSIX posix) {
+        super(posix, layout);
+    }
+
+    public long atime() {
+        return layout.st_atim.get(memory);
+    }
+
+    public long blocks() {
+        return layout.st_blocks.get(memory);
+    }
+
+    public long blockSize() {
+        return layout.st_blksize.get(memory);
+    }
+
+    public long ctime() {
+        return layout.st_ctim.get(memory);
+    }
+
+    public long dev() {
+        return layout.st_dev.get(memory);
+    }
+
+    public int gid() {
+        return layout.st_gid.get(memory);
+    }
+
+    public long ino() {
+        return layout.st_ino.get(memory);
+    }
+
+    public int mode() {
+        return layout.st_mode.get(memory) & 0xffff;
+    }
+
+    public long mtime() {
+        return layout.st_mtim.get(memory);
+    }
+
+    public int nlink() {
+        return layout.st_nlink.get(memory);
+    }
+
+    public long rdev() {
+        return layout.st_rdev.get(memory);
+    }
+
+    public long st_size() {
+        return layout.st_size.get(memory);
+    }
+
+    public int uid() {
+        return layout.st_uid.get(memory);
+    }
+
+    @Override
+    public long aTimeNanoSecs() {
+        return layout.st_atimnsec.get(memory);
+    }
+
+    @Override
+    public long cTimeNanoSecs() {
+        return layout.st_ctimnsec.get(memory);
+    }
+
+    @Override
+    public long mTimeNanoSecs() {
+        return layout.st_mtimnsec.get(memory);
+    }
+}
diff --git a/src/main/java/jnr/posix/DragonFlyPOSIX.java b/src/main/java/jnr/posix/DragonFlyPOSIX.java
new file mode 100644
index 0000000..a8ba316
--- /dev/null
+++ b/src/main/java/jnr/posix/DragonFlyPOSIX.java
@@ -0,0 +1,92 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: EPL 2.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Eclipse Public
+ * License Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+/**
+ * $Id: $
+ */
+
+package jnr.posix;
+
+import java.nio.ByteBuffer;
+import jnr.constants.platform.Confstr;
+import jnr.constants.platform.Pathconf;
+import jnr.constants.platform.Sysconf;
+import jnr.ffi.Memory;
+import jnr.ffi.mapper.FromNativeContext;
+import jnr.ffi.Pointer;
+import jnr.posix.util.MethodName;
+
+final class DragonFlyPOSIX extends BaseNativePOSIX {
+    DragonFlyPOSIX(LibCProvider libc, POSIXHandler handler) {
+        super(libc, handler);
+    }
+
+    public FileStat allocateStat() {
+        return new DragonFlyFileStat(this);
+    }
+
+    public MsgHdr allocateMsgHdr() {
+        handler.unimplementedError(MethodName.getCallerMethodName());
+        return null;
+    }
+
+    public SocketMacros socketMacros() {
+        handler.unimplementedError(MethodName.getCallerMethodName());
+        return null;
+    }
+
+    public long sysconf(Sysconf name) {
+        return libc().sysconf(name);
+    }
+
+    public int confstr(Confstr name, ByteBuffer buf, int len) {
+        return libc().confstr(name, buf, len);
+    }
+
+    public int fpathconf(int fd, Pathconf name) {
+        return libc().fpathconf(fd, name);
+    }
+
+    public Times times() {
+        return NativeTimes.times(this);
+    }
+
+
+    public static final PointerConverter PASSWD = new PointerConverter() {
+        public Object fromNative(Object arg, FromNativeContext ctx) {
+            return arg != null ? new DragonFlyPasswd((Pointer) arg) : null;
+        }
+    };
+
+    public Pointer allocatePosixSpawnFileActions() {
+        return Memory.allocateDirect(getRuntime(), 8);
+    }
+
+    public Pointer allocatePosixSpawnattr() {
+        return Memory.allocateDirect(getRuntime(), 8);
+    }
+}
diff --git a/src/main/java/jnr/posix/DragonFlyPasswd.java b/src/main/java/jnr/posix/DragonFlyPasswd.java
new file mode 100644
index 0000000..8876b70
--- /dev/null
+++ b/src/main/java/jnr/posix/DragonFlyPasswd.java
@@ -0,0 +1,101 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: EPL 2.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Eclipse Public
+ * License Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+/**
+ * $Id: $
+ */
+
+package jnr.posix;
+
+
+import jnr.ffi.StructLayout;
+
+public class DragonFlyPasswd extends NativePasswd implements Passwd {
+    private static final class Layout extends StructLayout {
+
+        private Layout(jnr.ffi.Runtime runtime) {
+            super(runtime);
+        }
+
+        public final UTF8StringRef pw_name = new UTF8StringRef();   // user name
+        public final UTF8StringRef pw_passwd = new UTF8StringRef(); // password (encrypted)
+        public final Signed32 pw_uid = new Signed32();       // user id
+        public final Signed32 pw_gid = new Signed32();       // user id
+        public final SignedLong pw_change = new SignedLong();// password change time
+        public final UTF8StringRef pw_class = new UTF8StringRef();  // user access class
+        public final UTF8StringRef pw_gecos = new UTF8StringRef();  // login info
+        public final UTF8StringRef pw_dir = new UTF8StringRef();    // home directory
+        public final UTF8StringRef pw_shell = new UTF8StringRef();  // default shell
+        public final SignedLong pw_expire = new SignedLong();    // account expiration
+        public final Signed32 pw_fields = new Signed32();    // internal: fields filled in
+    }
+    private static final Layout layout = new Layout(jnr.ffi.Runtime.getSystemRuntime());
+
+    DragonFlyPasswd(jnr.ffi.Pointer memory) {
+        super(memory);
+    }
+
+    public java.lang.String getAccessClass() {
+        return layout.pw_class.get(memory);
+    }
+
+    public java.lang.String getGECOS() {
+        return layout.pw_gecos.get(memory);
+    }
+
+    public long getGID() {
+        return layout.pw_gid.get(memory);
+    }
+
+    public java.lang.String getHome() {
+        return layout.pw_dir.get(memory);
+    }
+
+    public java.lang.String getLoginName() {
+        return layout.pw_name.get(memory);
+    }
+
+    public int getPasswdChangeTime() {
+        return layout.pw_change.intValue(memory);
+    }
+
+    public java.lang.String getPassword() {
+        return layout.pw_passwd.get(memory);
+    }
+
+    public java.lang.String getShell() {
+        return layout.pw_shell.get(memory);
+    }
+
+    public long getUID() {
+        return layout.pw_uid.get(memory);
+    }
+
+    public int getExpire() {
+        return layout.pw_expire.intValue(memory);
+    }
+}
diff --git a/src/main/java/jnr/posix/FileStat.java b/src/main/java/jnr/posix/FileStat.java
new file mode 100644
index 0000000..992bae7
--- /dev/null
+++ b/src/main/java/jnr/posix/FileStat.java
@@ -0,0 +1,73 @@
+package jnr.posix;
+
+public interface FileStat {
+    public static final int S_IFIFO = 0010000;  // named pipe (fifo)
+    public static final int S_IFCHR = 0020000;  // character special
+    public static final int S_IFDIR = 0040000;  // directory
+    public static final int S_IFBLK = 0060000;  // block special
+    public static final int S_IFREG = 0100000;  // regular
+    public static final int S_IFLNK = 0120000;  // symbolic link
+    public static final int S_IFSOCK = 0140000; // socket
+    public static final int S_IFMT = 0170000;   // file mask for type checks
+    public static final int S_ISUID = 0004000;  // set user id on execution
+    public static final int S_ISGID = 0002000;  // set group id on execution
+    public static final int S_ISVTX = 0001000;  // save swapped text even after use
+    public static final int S_IRUSR = 0000400;  // read permission, owner
+    public static final int S_IWUSR = 0000200;  // write permission, owner
+    public static final int S_IXUSR = 0000100;  // execute/search permission, owner
+    public static final int S_IRGRP = 0000040;  // read permission, group
+    public static final int S_IWGRP = 0000020;  // write permission, group
+    public static final int S_IXGRP = 0000010;  // execute/search permission, group
+    public static final int S_IROTH = 0000004;  // read permission, other
+    public static final int S_IWOTH = 0000002;  // write permission, other
+    public static final int S_IXOTH = 0000001;  // execute permission, other
+    
+    public static final int ALL_READ = S_IRUSR | S_IRGRP | S_IROTH;
+    public static final int ALL_WRITE = S_IWUSR | S_IWGRP | S_IWOTH;
+    public static final int S_IXUGO = S_IXUSR | S_IXGRP | S_IXOTH;
+
+    public long atime();
+    public long blocks();
+    public long blockSize();
+    public long ctime();
+    public long dev();
+    public String ftype();
+    public int gid();
+    public boolean groupMember(int gid);
+    public long ino();
+    public boolean isBlockDev();
+    public boolean isCharDev();
+    public boolean isDirectory();
+    public boolean isEmpty();
+    public boolean isExecutable();
+    public boolean isExecutableReal();
+    public boolean isFifo();
+    public boolean isFile();
+    public boolean isGroupOwned();
+    public boolean isIdentical(FileStat other);
+    public boolean isNamedPipe();
+    public boolean isOwned();
+    public boolean isROwned();
+    public boolean isReadable();
+    public boolean isReadableReal();
+    public boolean isWritable();
+    public boolean isWritableReal();
+    public boolean isSetgid();
+    public boolean isSetuid();
+    public boolean isSocket();
+    public boolean isSticky();
+    public boolean isSymlink();
+    public int major(long dev);
+    public int minor(long dev);
+    public int mode();
+    public long mtime();
+    public int nlink();
+    public long rdev();
+    /**
+     * Note: Name 'st_size' since Structure has a 'size' method already
+     *
+     * @return size of the stat structure
+     */
+    public long st_size();
+    public int uid();
+}
diff --git a/src/main/java/jnr/posix/FileTime.java b/src/main/java/jnr/posix/FileTime.java
new file mode 100644
index 0000000..ddd402f
--- /dev/null
+++ b/src/main/java/jnr/posix/FileTime.java
@@ -0,0 +1,12 @@
+package jnr.posix;
+
+import jnr.ffi.Struct;
+
+public class FileTime extends Struct {
+    public final Unsigned32 dwLowDateTime = new Unsigned32();
+    public final Unsigned32 dwHighDateTime = new Unsigned32();
+    
+    FileTime(jnr.ffi.Runtime runtime) {
+        super(runtime);
+    }
+}
diff --git a/src/main/java/jnr/posix/Flock.java b/src/main/java/jnr/posix/Flock.java
new file mode 100644
index 0000000..47855d8
--- /dev/null
+++ b/src/main/java/jnr/posix/Flock.java
@@ -0,0 +1,20 @@
+package jnr.posix;
+
+import jnr.ffi.Struct;
+
+abstract public class Flock extends Struct {
+    public Flock(jnr.ffi.Runtime runtime) {
+        super(runtime);
+    }
+    public abstract void type(short type);
+    public abstract void whence(short whence);
+    public abstract void start(long start);
+    public abstract void len(long len);
+    public abstract void pid(int pid);
+
+    public abstract short type();
+    public abstract short whence();
+    public abstract long start();
+    public abstract long len();
+    public abstract int pid();
+}
diff --git a/src/main/java/jnr/posix/FreeBSDCmsgHdr.java b/src/main/java/jnr/posix/FreeBSDCmsgHdr.java
new file mode 100644
index 0000000..b9ae98c
--- /dev/null
+++ b/src/main/java/jnr/posix/FreeBSDCmsgHdr.java
@@ -0,0 +1,72 @@
+package jnr.posix;
+
+import jnr.ffi.Pointer;
+import jnr.ffi.Runtime;
+import jnr.ffi.StructLayout;
+
+/**
+ * @author Bob McWhirter
+ */
+class FreeBSDCmsgHdr extends BaseCmsgHdr {
+
+    public static class Layout extends StructLayout {
+
+        protected Layout(Runtime runtime) {
+            super(runtime);
+        }
+
+        public final socklen_t cmsg_len = new socklen_t();
+        public final Signed32 cmsg_level = new Signed32();
+        public final Signed32 cmsg_type = new Signed32();
+    }
+
+    public static final Layout layout = new Layout(Runtime.getSystemRuntime());
+
+    public FreeBSDCmsgHdr(NativePOSIX posix, Pointer memory) {
+        super(posix, memory);
+    }
+
+    public FreeBSDCmsgHdr(NativePOSIX posix, Pointer memory, int totalLen) {
+        super(posix, memory, totalLen);
+    }
+
+    public void setLevel(int level) {
+        layout.cmsg_level.set(this.memory, level);
+    }
+
+    public int getLevel() {
+        return layout.cmsg_level.get(this.memory);
+    }
+
+    public void setType(int type) {
+        layout.cmsg_type.set(this.memory, type);
+    }
+
+    public int getType() {
+        return layout.cmsg_type.get(this.memory);
+    }
+
+    public int getLen() {
+        return (int) layout.cmsg_len.get(this.memory);
+    }
+
+    void setLen(int len) {
+        layout.cmsg_len.set(this.memory, len);
+    }
+
+    public String toString(String indent) {
+        StringBuffer buf = new StringBuffer();
+
+        buf.append(indent).append("cmsg {\n");
+        buf.append(indent).append("  cmsg_len=").append(layout.cmsg_len.get(this.memory)).append("\n");
+        buf.append(indent).append("  cmsg_level=").append(layout.cmsg_level.get(this.memory)).append("\n");
+        buf.append(indent).append("  cmsg_type=").append(layout.cmsg_type.get(this.memory)).append("\n");
+        buf.append(indent).append("  cmsg_data=").append(getData()).append("\n");
+        buf.append(indent).append("}");
+        return buf.toString();
+    }
+
+    public String toString(){
+        return toString( "" );
+    }
+}
diff --git a/src/main/java/jnr/posix/FreeBSDFileStat.java b/src/main/java/jnr/posix/FreeBSDFileStat.java
new file mode 100644
index 0000000..ec01140
--- /dev/null
+++ b/src/main/java/jnr/posix/FreeBSDFileStat.java
@@ -0,0 +1,142 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: EPL 2.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Eclipse Public
+ * License Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+/**
+ * $Id: $
+ */
+
+package jnr.posix;
+
+import jnr.ffi.StructLayout;
+
+public final class FreeBSDFileStat extends BaseFileStat implements NanosecondFileStat {
+    private static final class Layout extends StructLayout {
+
+        private Layout(jnr.ffi.Runtime runtime) {
+            super(runtime);
+        }
+
+        public final class time_t extends SignedLong {}
+        public final class dev_t extends Signed32 {}
+
+        public final dev_t  st_dev = new dev_t();
+        public final Signed32  st_ino = new Signed32();
+        public final Signed16  st_mode = new Signed16();
+        public final Signed16  st_nlink = new Signed16();
+        public final Signed32  st_uid = new Signed32();
+        public final Signed32  st_gid = new Signed32();
+        public final dev_t  st_rdev = new dev_t();
+        public final time_t st_atime = new time_t();
+        public final SignedLong st_atimensec = new SignedLong();
+        public final time_t st_mtime = new time_t();
+        public final SignedLong st_mtimensec = new SignedLong();
+        public final time_t st_ctime = new time_t();
+        public final SignedLong st_ctimensec = new SignedLong();
+        public final Signed64  st_size = new Signed64();
+        public final Signed64  st_blocks = new Signed64();
+        public final Signed32  st_blksize = new Signed32();
+        public final Signed32  st_flags = new Signed32();
+        public final Signed32  st_gen = new Signed32();
+        public final Signed32  st_lspare = new Signed32();
+        public final time_t    st_birthtime = new time_t();
+        public final SignedLong st_birthtimensec = new SignedLong();
+        /* FIXME: This padding isn't quite correct */
+        public final Signed64  st_qspare0 = new Signed64();
+    }
+    private static final Layout layout = new Layout(jnr.ffi.Runtime.getSystemRuntime());
+
+    public FreeBSDFileStat(NativePOSIX posix) {
+        super(posix, layout);
+    }
+
+    public long atime() {
+        return layout.st_atime.get(memory);
+    }
+
+    public long blocks() {
+        return layout.st_blocks.get(memory);
+    }
+
+    public long blockSize() {
+        return layout.st_blksize.get(memory);
+    }
+
+    public long ctime() {
+        return layout.st_ctime.get(memory);
+    }
+
+    public long dev() {
+        return layout.st_dev.get(memory);
+    }
+
+    public int gid() {
+        return layout.st_gid.get(memory);
+    }
+
+    public long ino() {
+        return layout.st_ino.get(memory);
+    }
+
+    public int mode() {
+        return layout.st_mode.get(memory) & 0xffff;
+    }
+
+    public long mtime() {
+        return layout.st_mtime.get(memory);
+    }
+
+    public int nlink() {
+        return layout.st_nlink.get(memory);
+    }
+
+    public long rdev() {
+        return layout.st_rdev.get(memory);
+    }
+
+    public long st_size() {
+        return layout.st_size.get(memory);
+    }
+
+    public int uid() {
+        return layout.st_uid.get(memory);
+    }
+
+    @Override
+    public long aTimeNanoSecs() {
+        return layout.st_atimensec.get(memory);
+    }
+
+    @Override
+    public long cTimeNanoSecs() {
+        return layout.st_ctimensec.get(memory);
+    }
+
+    @Override
+    public long mTimeNanoSecs() {
+        return layout.st_mtimensec.get(memory);
+    }
+}
diff --git a/src/main/java/jnr/posix/FreeBSDFileStat12.java b/src/main/java/jnr/posix/FreeBSDFileStat12.java
new file mode 100644
index 0000000..8e4633f
--- /dev/null
+++ b/src/main/java/jnr/posix/FreeBSDFileStat12.java
@@ -0,0 +1,145 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: EPL 2.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Eclipse Public
+ * License Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+/**
+ * $Id: $
+ */
+
+package jnr.posix;
+
+import jnr.ffi.StructLayout;
+
+public final class FreeBSDFileStat12 extends BaseFileStat implements NanosecondFileStat {
+    private static final class Layout extends StructLayout {
+
+        private Layout(jnr.ffi.Runtime runtime) {
+            super(runtime);
+        }
+
+        public final class time_t extends SignedLong {}
+        public final class dev_t extends Signed32 {}
+
+        public final dev_t  st_dev = new dev_t();
+        public final Signed64  st_ino = new Signed64();
+        // FIXME: this should be a 64-bit field
+        public final Signed32  st_nlink_upper = new Signed32();
+        public final Signed32  st_nlink = new Signed32();
+        public final Signed16  st_mode = new Signed16();
+        public final Signed16  st_padding0 = new Signed16();
+        public final Signed32  st_uid = new Signed32();
+        public final Signed32  st_gid = new Signed32();
+        public final Signed32  st_padding1 = new Signed32();
+        public final dev_t  st_rdev = new dev_t();
+        public final time_t st_atime = new time_t();
+        public final SignedLong st_atimensec = new SignedLong();
+        public final time_t st_mtime = new time_t();
+        public final SignedLong st_mtimensec = new SignedLong();
+        public final time_t st_ctime = new time_t();
+        public final SignedLong st_ctimensec = new SignedLong();
+        public final time_t    st_birthtime = new time_t();
+        public final SignedLong st_birthtimensec = new SignedLong();
+        public final Signed64  st_size = new Signed64();
+        public final Signed64  st_blocks = new Signed64();
+        public final Signed32  st_blksize = new Signed32();
+        public final Signed32  st_flags = new Signed32();
+        public final Signed64  st_gen = new Signed64();
+        /* FIXME: This padding isn't quite correct */
+        public final Signed64  st_qspare0 = new Signed64();
+    }
+    private static final Layout layout = new Layout(jnr.ffi.Runtime.getSystemRuntime());
+
+    public FreeBSDFileStat12(NativePOSIX posix) {
+        super(posix, layout);
+    }
+
+    public long atime() {
+        return layout.st_atime.get(memory);
+    }
+
+    public long blocks() {
+        return layout.st_blocks.get(memory);
+    }
+
+    public long blockSize() {
+        return layout.st_blksize.get(memory);
+    }
+
+    public long ctime() {
+        return layout.st_ctime.get(memory);
+    }
+
+    public long dev() {
+        return layout.st_dev.get(memory);
+    }
+
+    public int gid() {
+        return layout.st_gid.get(memory);
+    }
+
+    public long ino() {
+        return layout.st_ino.get(memory);
+    }
+
+    public int mode() {
+        return layout.st_mode.get(memory) & 0xffff;
+    }
+
+    public long mtime() {
+        return layout.st_mtime.get(memory);
+    }
+
+    public int nlink() {
+        return layout.st_nlink.get(memory);
+    }
+
+    public long rdev() {
+        return layout.st_rdev.get(memory);
+    }
+
+    public long st_size() {
+        return layout.st_size.get(memory);
+    }
+
+    public int uid() {
+        return layout.st_uid.get(memory);
+    }
+
+    @Override
+    public long aTimeNanoSecs() {
+        return layout.st_atimensec.get(memory);
+    }
+
+    @Override
+    public long cTimeNanoSecs() {
+        return layout.st_ctimensec.get(memory);
+    }
+
+    @Override
+    public long mTimeNanoSecs() {
+        return layout.st_mtimensec.get(memory);
+    }
+}
diff --git a/src/main/java/jnr/posix/FreeBSDMsgHdr.java b/src/main/java/jnr/posix/FreeBSDMsgHdr.java
new file mode 100644
index 0000000..92e7f20
--- /dev/null
+++ b/src/main/java/jnr/posix/FreeBSDMsgHdr.java
@@ -0,0 +1,151 @@
+package jnr.posix;
+
+import java.util.ArrayList;
+import java.util.List;
+import jnr.ffi.Pointer;
+import jnr.ffi.Runtime;
+import jnr.ffi.StructLayout;
+
+/**
+ * @author Bob McWhirter
+ */
+class FreeBSDMsgHdr extends BaseMsgHdr {
+
+    public static class Layout extends StructLayout {
+        protected Layout(Runtime runtime) {
+            super(runtime);
+        }
+
+        public final Pointer msg_name = new Pointer();
+        public final socklen_t msg_namelen = new socklen_t();
+        public final Pointer msg_iov = new Pointer();
+        public final Signed32 msg_iovlen = new Signed32();
+        public final Pointer msg_control = new Pointer();
+        public final socklen_t msg_controllen = new socklen_t();
+        public final Signed32 msg_flags = new Signed32();
+    }
+
+    private static final Layout layout = new Layout(Runtime.getSystemRuntime());
+
+    protected FreeBSDMsgHdr(NativePOSIX posix) {
+        super(posix, layout);
+        setName(null);
+    }
+
+    CmsgHdr allocateCmsgHdrInternal(NativePOSIX posix, Pointer pointer, int len) {
+        if (len > 0) {
+            return new FreeBSDCmsgHdr(posix, pointer, len);
+        } else {
+            return new FreeBSDCmsgHdr(posix, pointer);
+        }
+    }
+
+    @Override
+    void setControlPointer(Pointer control) {
+        layout.msg_control.set(this.memory, control);
+    }
+
+    @Override
+    void setControlLen(int len) {
+        layout.msg_controllen.set(this.memory, len);
+    }
+
+    public String toString() {
+        StringBuffer buf = new StringBuffer();
+        buf.append("msghdr {\n");
+        buf.append("  msg_name=").append(getName()).append(",\n");
+        buf.append("  msg_namelen=").append(getNameLen()).append(",\n");
+
+        buf.append("  msg_iov=[\n");
+        Pointer iovp = layout.msg_iov.get(this.memory);
+
+        int numIov = getIovLen();
+        for (int i = 0; i < numIov; ++i) {
+            Pointer eachp = iovp.slice(i * BaseIovec.layout.size());
+            buf.append(new BaseIovec(posix, eachp).toString("    "));
+            if (i < (numIov - 1)) {
+                buf.append(",\n");
+            } else {
+                buf.append("\n");
+            }
+        }
+        buf.append("  ],\n");
+
+        buf.append("  msg_control=[\n");
+
+        CmsgHdr[] controls = getControls();
+        for (int i = 0; i < controls.length; ++i) {
+            buf.append(((FreeBSDCmsgHdr) controls[i]).toString("    "));
+            if (i < controls.length - 1) {
+                buf.append(",\n");
+            } else {
+                buf.append("\n");
+            }
+        }
+        buf.append("  ],\n");
+        buf.append("  msg_controllen=").append(layout.msg_controllen.get(this.memory)).append("\n");
+
+        buf.append("  msg_iovlen=").append(getIovLen()).append(",\n");
+        buf.append("  msg_flags=").append(getFlags()).append(",\n");
+        buf.append("}");
+        return buf.toString();
+    }
+
+    @Override
+    void setNamePointer(Pointer name) {
+        layout.msg_name.set( this.memory, name );
+    }
+
+    @Override
+    Pointer getNamePointer() {
+        return layout.msg_name.get( this.memory );
+    }
+
+
+    @Override
+    void setNameLen(int len) {
+        layout.msg_namelen.set(this.memory, len);
+    }
+
+    @Override
+    int getNameLen() {
+        return (int) layout.msg_namelen.get(this.memory);
+    }
+
+    @Override
+    void setIovPointer(Pointer iov) {
+        layout.msg_iov.set(this.memory, iov);
+    }
+
+    @Override
+    Pointer getIovPointer() {
+        return layout.msg_iov.get( this.memory );
+    }
+
+    @Override
+    void setIovLen(int len) {
+        layout.msg_iovlen.set(this.memory, len);
+    }
+
+    @Override
+    int getIovLen() {
+        return (int) layout.msg_iovlen.get(this.memory);
+    }
+
+    @Override
+    Pointer getControlPointer() {
+        return layout.msg_control.get(this.memory);
+    }
+
+    public int getControlLen() {
+        return (int) layout.msg_controllen.get(this.memory);
+    }
+
+    public void setFlags(int flags) {
+        layout.msg_flags.set(this.memory, flags);
+    }
+
+    public int getFlags() {
+        return layout.msg_flags.get(this.memory);
+    }
+}
diff --git a/src/main/java/jnr/posix/FreeBSDPOSIX.java b/src/main/java/jnr/posix/FreeBSDPOSIX.java
new file mode 100644
index 0000000..ebb05d8
--- /dev/null
+++ b/src/main/java/jnr/posix/FreeBSDPOSIX.java
@@ -0,0 +1,121 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: EPL 2.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Eclipse Public
+ * License Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+/**
+ * $Id: $
+ */
+
+package jnr.posix;
+
+import jnr.constants.platform.Sysconf;
+import jnr.ffi.Memory;
+import jnr.ffi.mapper.FromNativeContext;
+import jnr.ffi.Pointer;
+import jnr.posix.util.MethodName;
+
+import java.lang.Runtime;
+import java.io.InputStreamReader;
+import java.io.BufferedReader;
+import java.nio.ByteBuffer;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import jnr.constants.platform.Confstr;
+import jnr.constants.platform.Pathconf;
+
+final class FreeBSDPOSIX extends BaseNativePOSIX {
+    private final int freebsdVersion;
+
+    FreeBSDPOSIX(LibCProvider libc, POSIXHandler handler) {
+        super(libc, handler);
+
+        int parsed_version = 0;
+
+        // FreeBSD 12 introduces a new stat structure. Until jffi gets dlvsym() support
+        // to allow us to link explicitly to supported versions of functions, detect
+        // the current userspace version and cross our fingers.
+        try {
+            Process p = Runtime.getRuntime().exec("/bin/freebsd-version -u");
+            String version = new BufferedReader(new InputStreamReader(p.getInputStream())).readLine();
+
+            if (p.waitFor() == 0 && version != null) {
+                NumberFormat fmt = NumberFormat.getIntegerInstance();
+                fmt.setGroupingUsed(false);
+
+                parsed_version = fmt.parse(version, new ParsePosition(0)).intValue();
+            }
+        } catch (Exception e) { }
+
+        freebsdVersion = parsed_version;
+    }
+
+    public FileStat allocateStat() {
+        if (freebsdVersion >= 12) {
+            return new FreeBSDFileStat12(this);
+        } else {
+            return new FreeBSDFileStat(this);
+        }
+    }
+
+    public MsgHdr allocateMsgHdr() {
+        return new FreeBSDMsgHdr(this);
+    }
+
+    public SocketMacros socketMacros() {
+        return FreeBSDSocketMacros.INSTANCE;
+    }
+
+    public long sysconf(Sysconf name) {
+        return libc().sysconf(name);
+    }
+
+    public int confstr(Confstr name, ByteBuffer buf, int len) {
+        return libc().confstr(name, buf, len);
+    }
+
+    public int fpathconf(int fd, Pathconf name) {
+        return libc().fpathconf(fd, name);
+    }
+
+    public Times times() {
+        return NativeTimes.times(this);
+    }
+
+
+    public static final PointerConverter PASSWD = new PointerConverter() {
+        public Object fromNative(Object arg, FromNativeContext ctx) {
+            return arg != null ? new FreeBSDPasswd((Pointer) arg) : null;
+        }
+    };
+
+    public Pointer allocatePosixSpawnFileActions() {
+        return Memory.allocateDirect(getRuntime(), 8);
+    }
+
+    public Pointer allocatePosixSpawnattr() {
+        return Memory.allocateDirect(getRuntime(), 8);
+    }
+}
diff --git a/src/main/java/jnr/posix/FreeBSDPasswd.java b/src/main/java/jnr/posix/FreeBSDPasswd.java
new file mode 100644
index 0000000..3345162
--- /dev/null
+++ b/src/main/java/jnr/posix/FreeBSDPasswd.java
@@ -0,0 +1,101 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: EPL 2.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Eclipse Public
+ * License Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+/**
+ * $Id: $
+ */
+
+package jnr.posix;
+
+
+import jnr.ffi.StructLayout;
+
+public class FreeBSDPasswd extends NativePasswd implements Passwd {
+    private static final class Layout extends StructLayout {
+
+        private Layout(jnr.ffi.Runtime runtime) {
+            super(runtime);
+        }
+
+        public final UTF8StringRef pw_name = new UTF8StringRef();   // user name
+        public final UTF8StringRef pw_passwd = new UTF8StringRef(); // password (encrypted)
+        public final Signed32 pw_uid = new Signed32();       // user id
+        public final Signed32 pw_gid = new Signed32();       // user id
+        public final SignedLong pw_change = new SignedLong();// password change time
+        public final UTF8StringRef pw_class = new UTF8StringRef();  // user access class
+        public final UTF8StringRef pw_gecos = new UTF8StringRef();  // login info
+        public final UTF8StringRef pw_dir = new UTF8StringRef();    // home directory
+        public final UTF8StringRef pw_shell = new UTF8StringRef();  // default shell
+        public final SignedLong pw_expire = new SignedLong();    // account expiration
+        public final Signed32 pw_fields = new Signed32();    // internal: fields filled in
+    }
+    private static final Layout layout = new Layout(jnr.ffi.Runtime.getSystemRuntime());
+
+    FreeBSDPasswd(jnr.ffi.Pointer memory) {
+        super(memory);
+    }
+
+    public java.lang.String getAccessClass() {
+        return layout.pw_class.get(memory);
+    }
+
+    public java.lang.String getGECOS() {
+        return layout.pw_gecos.get(memory);
+    }
+
+    public long getGID() {
+        return layout.pw_gid.get(memory);
+    }
+
+    public java.lang.String getHome() {
+        return layout.pw_dir.get(memory);
+    }
+
+    public java.lang.String getLoginName() {
+        return layout.pw_name.get(memory);
+    }
+
+    public int getPasswdChangeTime() {
+        return layout.pw_change.intValue(memory);
+    }
+
+    public java.lang.String getPassword() {
+        return layout.pw_passwd.get(memory);
+    }
+
+    public java.lang.String getShell() {
+        return layout.pw_shell.get(memory);
+    }
+
+    public long getUID() {
+        return layout.pw_uid.get(memory);
+    }
+
+    public int getExpire() {
+        return layout.pw_expire.intValue(memory);
+    }
+}
diff --git a/src/main/java/jnr/posix/FreeBSDSocketMacros.java b/src/main/java/jnr/posix/FreeBSDSocketMacros.java
new file mode 100644
index 0000000..c254d38
--- /dev/null
+++ b/src/main/java/jnr/posix/FreeBSDSocketMacros.java
@@ -0,0 +1,29 @@
+package jnr.posix;
+
+import jnr.ffi.*;
+import jnr.ffi.Runtime;
+
+/**
+ * @author Bob McWhirter
+ */
+public class FreeBSDSocketMacros implements SocketMacros {
+
+    public static final FreeBSDSocketMacros INSTANCE = new FreeBSDSocketMacros();
+
+    public int CMSG_ALIGN(int len) {
+        int sizeof_size_t = Runtime.getSystemRuntime().findType(TypeAlias.size_t).size();
+        return (len + sizeof_size_t - 1) & ~(sizeof_size_t - 1);
+    }
+
+    public int CMSG_SPACE(int l) {
+        return CMSG_ALIGN(FreeBSDCmsgHdr.layout.size()) + CMSG_ALIGN(l);
+    }
+
+    public int CMSG_LEN(int l) {
+        return CMSG_ALIGN( FreeBSDCmsgHdr.layout.size() ) + (l);
+    }
+
+    public Pointer CMSG_DATA(Pointer cmsg) {
+        return cmsg.slice(CMSG_ALIGN(FreeBSDCmsgHdr.layout.size()));
+    }
+}
diff --git a/src/main/java/jnr/posix/Group.java b/src/main/java/jnr/posix/Group.java
new file mode 100644
index 0000000..dd9621b
--- /dev/null
+++ b/src/main/java/jnr/posix/Group.java
@@ -0,0 +1,8 @@
+package jnr.posix;
+
+public interface Group {
+    public String getName();
+    public String getPassword();
+    public long getGID();
+    public String[] getMembers();
+}
diff --git a/src/main/java/jnr/posix/HANDLE.java b/src/main/java/jnr/posix/HANDLE.java
new file mode 100644
index 0000000..825f808
--- /dev/null
+++ b/src/main/java/jnr/posix/HANDLE.java
@@ -0,0 +1,46 @@
+package jnr.posix;
+
+import jnr.ffi.Pointer;
+import jnr.ffi.mapper.DataConverter;
+import jnr.ffi.mapper.FromNativeContext;
+import jnr.ffi.mapper.ToNativeContext;
+
+public final class HANDLE {
+    public static final long INVALID_HANDLE_VALUE = -1L;
+    private final Pointer pointer;
+
+    public HANDLE(Pointer pointer) {
+        this.pointer = pointer;
+    }
+
+    public final Pointer toPointer() {
+        return pointer;
+    }
+
+    public final boolean isValid() {
+        return pointer.address() != (INVALID_HANDLE_VALUE & jnr.ffi.Runtime.getSystemRuntime().addressMask());
+    }
+
+    public static HANDLE valueOf(Pointer value) {
+        return new HANDLE(value);
+    }
+
+    public static HANDLE valueOf(long value) {
+        return new HANDLE(jnr.ffi.Runtime.getSystemRuntime().getMemoryManager().newPointer(value));
+    }
+
+    public static final DataConverter<HANDLE, Pointer> Converter = new DataConverter<HANDLE, Pointer>() {
+
+        public Pointer toNative(HANDLE value, ToNativeContext context) {
+            return value != null ? value.pointer : null;
+        }
+
+        public HANDLE fromNative(Pointer nativeValue, FromNativeContext context) {
+            return nativeValue != null ? new HANDLE(nativeValue) : null;
+        }
+
+        public Class<Pointer> nativeType() {
+            return Pointer.class;
+        }
+    };
+}
diff --git a/src/main/java/jnr/posix/Iovec.java b/src/main/java/jnr/posix/Iovec.java
new file mode 100644
index 0000000..310d878
--- /dev/null
+++ b/src/main/java/jnr/posix/Iovec.java
@@ -0,0 +1,12 @@
+package jnr.posix;
+
+import java.nio.ByteBuffer;
+
+/**
+ * @author Bob McWhirter
+ */
+public interface Iovec {
+
+    ByteBuffer get();
+    void set(ByteBuffer buf);
+}
diff --git a/src/main/java/jnr/posix/JavaFileStat.java b/src/main/java/jnr/posix/JavaFileStat.java
new file mode 100644
index 0000000..e6991ec
--- /dev/null
+++ b/src/main/java/jnr/posix/JavaFileStat.java
@@ -0,0 +1,317 @@
+package jnr.posix;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.DosFileAttributes;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.attribute.PosixFileAttributes;
+import java.nio.file.attribute.PosixFilePermission;
+import java.util.Set;
+
+public class JavaFileStat extends AbstractJavaFileStat {
+    short st_mode;
+
+    BasicFileAttributes attrs;
+    PosixFileAttributes posixAttrs;
+    DosFileAttributes dosAttrs;
+    
+    public JavaFileStat(POSIX posix, POSIXHandler handler) {
+        super(posix, handler);
+    }
+    
+    public void setup(String filePath) {
+        File file = new JavaSecuredFile(filePath);
+        Path path = file.toPath();
+
+        try {
+            try {
+                // try POSIX
+                posixAttrs = Files.readAttributes(path, PosixFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
+                attrs = posixAttrs;
+            } catch (UnsupportedOperationException uoe) {
+                try {
+                    // try DOS
+                    dosAttrs = Files.readAttributes(path, DosFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
+                    attrs = dosAttrs;
+                } catch (UnsupportedOperationException uoe2) {
+                    attrs = Files.readAttributes(path, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
+                }
+            }
+        } catch (IOException ioe) {
+            // fall back on pre-NIO2 logic
+            attrs = new PreNIO2FileAttributes(file);
+        }
+
+        // Simulated mode value
+        st_mode = calculateMode(file, (short) 0);
+    }
+
+    private class PreNIO2FileAttributes implements BasicFileAttributes {
+        final long st_size;
+        final int st_ctime;
+        final int st_mtime;
+        final boolean regularFile;
+        final boolean directory;
+
+        PreNIO2FileAttributes(File file) {
+            st_size = file.length();
+
+            st_mtime = (int) (file.lastModified() / 1000);
+
+            // Parent file last modified will only represent when something was added or removed.
+            // This is not correct, but it is better than nothing and does work in one common use
+            // case.
+            if (file.getParentFile() != null) {
+                st_ctime = (int) (file.getParentFile().lastModified() / 1000);
+            } else {
+                st_ctime = st_mtime;
+            }
+
+            regularFile = file.isFile();
+            directory = file.isDirectory();
+        }
+
+        @Override
+        public FileTime lastModifiedTime() {
+            return FileTime.fromMillis(st_mtime);
+        }
+
+        @Override
+        public FileTime lastAccessTime() {
+            return lastModifiedTime();
+        }
+
+        @Override
+        public FileTime creationTime() {
+            return FileTime.fromMillis(st_mtime);
+        }
+
+        @Override
+        public boolean isRegularFile() {
+            return (st_mode & S_IFREG) != 0;
+        }
+
+        @Override
+        public boolean isDirectory() {
+            return (st_mode & S_IFDIR) != 0;
+        }
+
+        @Override
+        public boolean isSymbolicLink() {
+            return (st_mode & S_IFLNK) != 0;
+        }
+
+        @Override
+        public boolean isOther() {
+            return !(isRegularFile() || isDirectory() || isSymbolicLink());
+        }
+
+        @Override
+        public long size() {
+            return st_size;
+        }
+
+        @Override
+        public Object fileKey() {
+            return null;
+        }
+    }
+
+    private short calculateMode(File file, short st_mode) {
+        // implementation to lowest common denominator...
+        // Windows has no file mode, but C ruby returns either 0100444 or 0100644
+
+        if (file.canRead()) {
+            st_mode |= ALL_READ;
+        }
+
+        if (file.canWrite()) {
+            st_mode |= ALL_WRITE;
+            st_mode &= ~(S_IWGRP | S_IWOTH);
+        }
+
+        if (file.isDirectory()) {
+            st_mode |= S_IFDIR;
+        } else if (file.isFile()) {
+            st_mode |= S_IFREG;
+        }
+
+        if (posixAttrs != null && posixAttrs.isSymbolicLink()) {
+            st_mode |= S_IFLNK;
+        } else {
+            try {
+                st_mode = calculateSymlink(file, st_mode);
+            } catch (IOException e) {
+                // Not sure we can do much in this case...
+            }
+        }
+
+        return st_mode;
+    }
+
+    private static short calculateSymlink(File file, short st_mode) throws IOException {
+        if (file.getAbsoluteFile().getParentFile() == null) {
+            return st_mode;
+        }
+
+        File absoluteParent = file.getAbsoluteFile().getParentFile();
+        File canonicalParent = absoluteParent.getCanonicalFile();
+
+        if (canonicalParent.getAbsolutePath().equals(absoluteParent.getAbsolutePath())) {
+            // parent doesn't change when canonicalized, compare absolute and canonical file directly
+            if (!file.getAbsolutePath().equalsIgnoreCase(file.getCanonicalPath())) {
+                st_mode |= S_IFLNK;
+                return st_mode;
+            }
+        }
+
+        // directory itself has symlinks (canonical != absolute), so build new path with canonical parent and compare
+        file = new JavaSecuredFile(canonicalParent.getAbsolutePath() + "/" + file.getName());
+        if (!file.getAbsolutePath().equalsIgnoreCase(file.getCanonicalPath())) {
+            st_mode |= S_IFLNK;
+        }
+
+        return st_mode;
+    }
+
+    /**
+     * Limitation: Java has no access time support, so we return mtime as the next best thing.
+     */
+    public long atime() {
+        return (int) (attrs.lastAccessTime().toMillis() / 1000);
+    }
+
+    public long ctime() {
+        return (int) (attrs.creationTime().toMillis() / 1000);
+    }
+
+    public boolean isDirectory() {
+        return attrs.isDirectory();
+    }
+
+    public boolean isEmpty() {
+        return attrs.size() == 0;
+    }
+
+    public boolean isExecutable() {
+        if (posixAttrs != null) {
+            Set<PosixFilePermission> permissions = posixAttrs.permissions();
+
+            return permissions.contains(PosixFilePermission.OWNER_EXECUTE) ||
+                    permissions.contains(PosixFilePermission.GROUP_EXECUTE) ||
+                    permissions.contains(PosixFilePermission.OTHERS_EXECUTE);
+        }
+
+        // silently return false, since it's likely an unusual filesystem
+        return false;
+    }
+
+    public boolean isExecutableReal() {
+        return isExecutable();
+    }
+
+    public boolean isFile() {
+        return attrs.isRegularFile();
+    }
+    
+    public boolean isGroupOwned() {
+        return groupMember(gid());
+    }
+
+    public boolean isIdentical(FileStat other) {
+        // if attrs supports file keys, we can compare them
+        Object key = attrs.fileKey();
+
+        if (key != null && other instanceof JavaFileStat) {
+            JavaFileStat otherStat = (JavaFileStat) other;
+
+            return key.equals(otherStat.attrs.fileKey());
+        }
+
+        handler.unimplementedError("identical file detection");
+        
+        return false;
+    }
+
+    public boolean isOwned() {
+        return posix.geteuid() == uid();
+    }
+
+    public boolean isROwned() {
+        return posix.getuid() == uid();
+    }
+
+    public boolean isReadable() {
+        if (posixAttrs != null) {
+            Set<PosixFilePermission> permissions = posixAttrs.permissions();
+
+            return permissions.contains(PosixFilePermission.OWNER_READ) ||
+                    permissions.contains(PosixFilePermission.GROUP_READ) ||
+                    permissions.contains(PosixFilePermission.OTHERS_READ);
+        }
+
+        int mode = mode();
+        
+        if ((mode & S_IRUSR) != 0) return true;
+        if ((mode & S_IRGRP) != 0) return true;
+        if ((mode & S_IROTH) != 0) return true;
+        
+        return false;
+    }
+
+    // We do both readable and readable_real through the same method because
+    public boolean isReadableReal() {
+        return isReadable();
+    }
+
+    public boolean isSymlink() {
+        if (posixAttrs != null) {
+            return posixAttrs.isSymbolicLink();
+        }
+
+        return (mode() & S_IFLNK) == S_IFLNK;
+    }
+    
+    public boolean isWritable() {
+        if (posixAttrs != null) {
+            Set<PosixFilePermission> permissions = posixAttrs.permissions();
+
+            return permissions.contains(PosixFilePermission.OWNER_WRITE) ||
+                    permissions.contains(PosixFilePermission.GROUP_WRITE) ||
+                    permissions.contains(PosixFilePermission.OTHERS_WRITE);
+        } else if (dosAttrs != null) {
+            return !dosAttrs.isReadOnly();
+        }
+
+        int mode = mode();
+        
+        if ((mode & S_IWUSR) != 0) return true;
+        if ((mode & S_IWGRP) != 0) return true;
+        if ((mode & S_IWOTH) != 0) return true;
+
+        return false;
+    }
+    
+    // We do both readable and readable_real through the same method because
+    // in our java process effective and real userid will always be the same.
+    public boolean isWritableReal() {
+        return isWritable();
+    }
+
+    public int mode() {
+        return st_mode & 0xffff;
+    }
+
+    public long mtime() {
+        return (int) (attrs.lastModifiedTime().toMillis() / 1000);
+    }
+
+    public long st_size() {
+        return attrs.size();
+    }
+
+}
diff --git a/src/main/java/jnr/posix/JavaLibCHelper.java b/src/main/java/jnr/posix/JavaLibCHelper.java
new file mode 100644
index 0000000..e8547ff
--- /dev/null
+++ b/src/main/java/jnr/posix/JavaLibCHelper.java
@@ -0,0 +1,528 @@
+ /*
+ **** BEGIN LICENSE BLOCK *****
+ * Version: EPL 2.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Eclipse Public
+ * License Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * Copyright (C) 2007 
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package jnr.posix;
+
+import static jnr.constants.platform.Errno.*;
+
+import java.io.*;
+import java.lang.management.ManagementFactory;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channel;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.regex.Pattern;
+
+import jnr.constants.platform.Errno;
+import jnr.posix.util.Chmod;
+import jnr.posix.util.ExecIt;
+import jnr.posix.util.JavaCrypt;
+import jnr.posix.util.Platform;
+
+ /**
+ * This libc implementation is created one per runtime instance versus the others which
+ * are expected to be one static instance for whole JVM.  Because of this it is no big
+ * deal to make reference to a POSIXHandler directly.
+ */
+// FIXME: we ignore all exceptions with shell launcher...should we do something better
+public class JavaLibCHelper {
+    public static final int STDIN = 0;
+    public static final int STDOUT = 1;
+    public static final int STDERR = 2;
+
+    private static final ThreadLocal<Integer> errno = new ThreadLocal<Integer>();
+
+    private final POSIXHandler handler;
+    private final Map<String, String> env;
+    
+
+    public JavaLibCHelper(POSIXHandler handler) {
+        this.env = new HashMap<String, String>();
+        this.handler = handler;
+    }
+    
+    private static class ReflectiveAccess {
+        private static final Class SEL_CH_IMPL;
+        private static final Method SEL_CH_IMPL_GET_FD;
+        private static final Class FILE_CHANNEL_IMPL;
+        private static final Field FILE_CHANNEL_IMPL_FD;
+        private static final Field FILE_DESCRIPTOR_FD;
+        private static final Field FILE_DESCRIPTOR_HANDLE;
+
+        static {
+            try {
+                Method getModule = Class.class.getMethod("getModule");
+                Class<?> Module = Class.forName("java.lang.Module");
+                Method isOpen = Module.getMethod("isOpen", String.class, Module);
+                Method isNamed = Module.getMethod("isNamed");
+                Method addOpens = Module.getMethod("addOpens", String.class, Module);
+                Object JNRPosixModule = getModule.invoke(ReflectiveAccess.class);
+                Object JavaBaseModule = getModule.invoke(FileDescriptor.class);
+
+                if (!((Boolean) isOpen.invoke(JavaBaseModule, "java.io", JNRPosixModule))) {
+                    // warn that many APIs will be broken without module access
+                    System.err.println("Some JDK modules may not be open to jnr-posix, which will break file descriptor and process APIs. See https://github.com/jnr/jnr-posix/wiki/Using-POSIX-with-Java-Modules");
+                } else if (!((Boolean) isNamed.invoke(JNRPosixModule))) {
+                    // explicitly open them to avoid the implicitly open warning
+                    addOpens.invoke(JavaBaseModule, "java.io", JNRPosixModule);
+                    addOpens.invoke(JavaBaseModule, "sun.nio.ch", JNRPosixModule);
+                }
+            } catch (Exception e) {
+                // ignore, we're not on Java 9+
+            }
+
+            Method getFD;
+            Class selChImpl;
+            try {
+                selChImpl = Class.forName("sun.nio.ch.SelChImpl");
+                try {
+                    getFD = selChImpl.getMethod("getFD");
+                    getFD.setAccessible(true);
+                } catch (Exception e) {
+                    getFD = null;
+                }
+            } catch (Exception e) {
+                selChImpl = null;
+                getFD = null;
+            }
+            SEL_CH_IMPL = selChImpl;
+            SEL_CH_IMPL_GET_FD = getFD;
+
+            Field fd;
+            Class fileChannelImpl;
+            try {
+                fileChannelImpl = Class.forName("sun.nio.ch.FileChannelImpl");
+                try {
+                    fd = fileChannelImpl.getDeclaredField("fd");
+                    fd.setAccessible(true);
+                } catch (Exception e) {
+                    fd = null;
+                }
+            } catch (Exception e) {
+                fileChannelImpl = null;
+                fd = null;
+            }
+            FILE_CHANNEL_IMPL = fileChannelImpl;
+            FILE_CHANNEL_IMPL_FD = fd;
+
+            Field ffd;
+            try {
+                ffd = FileDescriptor.class.getDeclaredField("fd");
+                ffd.setAccessible(true);
+            } catch (Exception e) {
+                ffd = null;
+            }
+            FILE_DESCRIPTOR_FD = ffd;
+
+            if (Platform.IS_WINDOWS) {
+                Field handle;
+                try {
+                    handle = FileDescriptor.class.getDeclaredField("handle");
+                    handle.setAccessible(true);
+                } catch (Exception e) {
+                    handle = null;
+                }
+                FILE_DESCRIPTOR_HANDLE = handle;
+            } else {
+                FILE_DESCRIPTOR_HANDLE = null;
+            }
+        }
+    }
+
+    public static FileDescriptor getDescriptorFromChannel(Channel channel) {
+        if (ReflectiveAccess.SEL_CH_IMPL_GET_FD != null && ReflectiveAccess.SEL_CH_IMPL.isInstance(channel)) {
+            // Pipe Source and Sink, Sockets, and other several other selectable channels
+            try {
+                return (FileDescriptor)ReflectiveAccess.SEL_CH_IMPL_GET_FD.invoke(channel);
+            } catch (Exception e) {
+                // return bogus below
+            }
+        } else if (ReflectiveAccess.FILE_CHANNEL_IMPL_FD != null && ReflectiveAccess.FILE_CHANNEL_IMPL.isInstance(channel)) {
+            // FileChannels
+            try {
+                return (FileDescriptor)ReflectiveAccess.FILE_CHANNEL_IMPL_FD.get(channel);
+            } catch (Exception e) {
+                // return bogus below
+            }
+        } else if (ReflectiveAccess.FILE_DESCRIPTOR_FD != null) {
+            // anything else that implements a getFD method that returns an int
+            FileDescriptor unixFD = new FileDescriptor();
+            
+                try {
+                    Method getFD = channel.getClass().getMethod("getFD");
+                    ReflectiveAccess.FILE_DESCRIPTOR_FD.set(unixFD, (Integer)getFD.invoke(channel));
+                    return unixFD;
+                } catch (Exception e) {
+                    // return bogus below
+                }
+        }
+        return new FileDescriptor();
+    }
+
+    static int errno() {
+        Integer errno = JavaLibCHelper.errno.get();
+        return errno != null ? errno : 0;
+    }
+
+    static void errno(int errno) {
+        JavaLibCHelper.errno.set(errno);
+    }
+
+    static void errno(Errno errno) {
+        JavaLibCHelper.errno.set(errno.intValue());
+    }
+    
+    public int chmod(String filename, int mode) {
+        return Chmod.chmod(new JavaSecuredFile(filename), Integer.toOctalString(mode));
+    }
+
+    public int chown(String filename, int user, int group) {
+        PosixExec launcher = new PosixExec(handler);
+        int chownResult = -1;
+        int chgrpResult = -1;
+        
+        try {
+            if (user != -1) chownResult = launcher.runAndWait("chown", "" + user, filename);
+            if (group != -1) chgrpResult = launcher.runAndWait("chgrp ", "" + user, filename);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        } catch (Exception e) {
+        }
+        
+        return chownResult != -1 && chgrpResult != -1 ? 0 : 1;
+    }
+
+    public static CharSequence crypt(CharSequence original, CharSequence salt) {
+        return JavaCrypt.crypt(original, salt);
+    }
+
+    // FIXME: This version has no idea what charset you want, so it just uses default.
+    public static byte[] crypt(byte[] original, byte[] salt) {
+        return JavaCrypt.crypt(new String(original), new String(salt)).toString().getBytes();
+    }
+
+    public int getfd(FileDescriptor descriptor) {
+        return getfdFromDescriptor(descriptor);
+    }
+
+    public static int getfdFromDescriptor(FileDescriptor descriptor) {
+        if (descriptor == null || ReflectiveAccess.FILE_DESCRIPTOR_FD == null) return -1;
+        try {
+            return ReflectiveAccess.FILE_DESCRIPTOR_FD.getInt(descriptor);
+        } catch (SecurityException e) {
+        } catch (IllegalArgumentException e) {
+        } catch (IllegalAccessException e) {
+        }
+
+        return -1;
+    }
+
+     public static HANDLE gethandle(FileDescriptor descriptor) {
+         if (descriptor == null || ReflectiveAccess.FILE_DESCRIPTOR_HANDLE == null) return HANDLE.valueOf(-1);
+         try {
+             return gethandle(ReflectiveAccess.FILE_DESCRIPTOR_HANDLE.getLong(descriptor));
+         } catch (SecurityException e) {
+         } catch (IllegalArgumentException e) {
+         } catch (IllegalAccessException e) {
+         }
+
+         return HANDLE.valueOf(-1);
+     }
+
+     public static HANDLE gethandle(long descriptor) {
+         return HANDLE.valueOf(descriptor);
+     }
+
+    public String getlogin() {
+        return System.getProperty("user.name");
+    }
+
+    public String gethostname() {
+        String hn = System.getenv("HOSTNAME");
+        if (hn == null) hn = System.getenv("COMPUTERNAME");
+        return hn;
+    }
+
+    public int getpid() {
+        try {
+            return handler.getPID();
+        } catch (UnsupportedOperationException uoe) {
+            // if handler raises UOE, as our default handler does, try other ways
+
+            // Java 9+ provide ProcessHandle.current
+            try {
+                Class processHandle = Class.forName("java.lang.ProcessHandle");
+                Object current = processHandle.getMethod("current").invoke(null); // static
+                return (int) (long) (Long) processHandle.getMethod("pid").invoke(current);
+            } catch (Exception e) {
+                // ignore, try Java 8 logic below
+            }
+
+            // Java 8- can use management beans to infer the pid
+            try {
+                String runtimeName = ManagementFactory.getRuntimeMXBean().getName();
+                int index = runtimeName.indexOf('@');
+
+                if (index > 0) {
+                    return (int) Long.parseLong(runtimeName.substring(0, index));
+                }
+            } catch (Exception e) {
+                // ignore, rethrow UOE below
+            }
+
+            // couldn't do it, rethrow
+            throw uoe;
+        }
+
+    }
+    ThreadLocal<Integer> pwIndex = new ThreadLocal<Integer>() {
+        @Override
+        protected Integer initialValue() {
+            return 0;
+        }
+    };
+    public Passwd getpwent() {
+        Passwd retVal = pwIndex.get().intValue() == 0 ? new JavaPasswd(handler) : null;
+        pwIndex.set(pwIndex.get() + 1);
+        return retVal;
+    }
+
+    public int setpwent() {
+        return 0;
+    }
+
+    public int endpwent() {
+        pwIndex.set(0);
+        return 0;
+    }
+    public Passwd getpwuid(int which) {
+        return which == JavaPOSIX.LoginInfo.UID ? new JavaPasswd(handler) : null;
+    }
+    public int isatty(int fd) {
+        return (fd == STDOUT || fd == STDIN || fd == STDERR) ? 1 : 0;
+    }
+
+    public int link(String oldpath, String newpath) {
+        try {
+            return new PosixExec(handler).runAndWait("ln", oldpath, newpath);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        } catch (Exception e) {
+        }
+        errno(EINVAL);
+        return -1;  // We tried and failed for some reason. Indicate error.
+    }
+    
+    public int lstat(String path, FileStat stat) {
+        File file = new JavaSecuredFile(path);
+
+        if (!file.exists()) {
+            errno(ENOENT);
+            return -1;
+        }
+        
+        // FIXME: Bulletproof this or no?
+        JavaFileStat jstat = (JavaFileStat) stat;
+        
+        jstat.setup(path);
+
+        // TODO: Add error reporting for cases we can calculate: ENOTDIR, ENAMETOOLONG, ENOENT
+        // EACCES, ELOOP, EFAULT, EIO
+
+        return 0;
+    }
+    
+    public int mkdir(String path, int mode) {
+        File dir = new JavaSecuredFile(path);
+        
+        if (!dir.mkdir()) return -1;
+
+        chmod(path, mode);
+        
+        return 0;
+    }
+
+    public int rmdir(String path) {
+        return new JavaSecuredFile(path).delete() ? 0 : -1;
+    }
+
+    public static int chdir(String path) {
+        System.setProperty("user.dir", path);
+        return 0;
+    }
+    
+    public int stat(String path, FileStat stat) {
+        // FIXME: Bulletproof this or no?
+        JavaFileStat jstat = (JavaFileStat) stat;
+        
+        try {
+            File file = new JavaSecuredFile(path);
+            
+            if (!file.exists()) {
+                errno(ENOENT);
+                return -1;
+            }
+
+            jstat.setup(file.getCanonicalPath());
+        } catch (IOException e) {
+            // TODO: Throw error when we have problems stat'ing canonicalizing
+        }
+
+        // TODO: Add error reporting for cases we can calculate: ENOTDIR, ENAMETOOLONG,
+        // EACCES, ELOOP, EFAULT, EIO
+
+        return 0;
+    }
+
+    public int symlink(String oldpath, String newpath) {
+        try {
+            return new PosixExec(handler).runAndWait("ln", "-s", oldpath, newpath);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        } catch (Exception e) {
+        }
+        errno(EEXIST);
+        return -1;  // We tried and failed for some reason. Indicate error.
+
+    }
+
+    public int readlink(String oldpath, ByteBuffer buffer, int length) throws IOException {
+        try {
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            new PosixExec(handler).runAndWait(baos, "readlink", oldpath);
+            
+            byte[] bytes = baos.toByteArray();
+            
+            if (bytes.length > length || bytes.length == 0) return -1;
+            buffer.put(bytes, 0, bytes.length - 1); // trim off \n
+
+            
+            return buffer.position();
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        }
+        errno(ENOENT);
+        return -1; // We tried and failed for some reason. Indicate error.
+    }
+
+    public Map<String, String> getEnv() {
+        return env;
+    }
+
+    public static FileDescriptor toFileDescriptor(int fileDescriptor) {
+        FileDescriptor descriptor = new FileDescriptor();
+        try {
+            ReflectiveAccess.FILE_DESCRIPTOR_FD.set(descriptor, fileDescriptor);
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException(e);
+        }
+        return descriptor;
+    }
+
+    public static FileDescriptor toFileDescriptor(HANDLE fileDescriptor) {
+        FileDescriptor descriptor = new FileDescriptor();
+        try {
+            ReflectiveAccess.FILE_DESCRIPTOR_HANDLE.set(descriptor, fileDescriptor.toPointer().address());
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException(e);
+        }
+        return descriptor;
+    }
+
+    private static class PosixExec extends ExecIt {
+        private final AtomicReference<Errno> errno = new AtomicReference<Errno>(Errno.EINVAL);
+        private final ErrnoParsingOutputStream errorStream = new ErrnoParsingOutputStream(errno);
+
+        public PosixExec(POSIXHandler handler) {
+            super(handler);
+        }
+
+        private int parseResult(int result) {
+            if (result == 0) {
+                return result;
+            }
+            errno(errno.get());
+            return -1;
+        }
+
+        public int runAndWait(String... args) throws IOException, InterruptedException {
+            return runAndWait(handler.getOutputStream(), errorStream, args);
+        }
+
+        public int runAndWait(OutputStream output, String... args) throws IOException, InterruptedException {
+            return runAndWait(output, errorStream, args);
+        }
+
+        public int runAndWait(OutputStream output, OutputStream error, String... args) throws IOException, InterruptedException {
+            return parseResult(super.runAndWait(output, error, args));
+        }
+    }
+
+    private static final class ErrnoParsingOutputStream extends OutputStream {
+        private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        private final AtomicReference<Errno> errno;
+
+        private ErrnoParsingOutputStream(AtomicReference<Errno> errno) {
+            this.errno = errno;
+        }
+
+        @Override
+        public void write(int b) throws IOException {
+            if (b != '\r' && b != '\n' && b != -1) {
+                baos.write(b);
+            } else if (baos.size() > 0) {
+                String errorString = baos.toString();
+                baos.reset();
+                parseError(errorString);
+            }
+        }
+
+        static Map<Pattern, Errno> errorPatterns = new HashMap<Pattern, Errno>();
+        static {
+            errorPatterns.put(Pattern.compile("File exists"), Errno.EEXIST);
+            errorPatterns.put(Pattern.compile("Operation not permitted"), Errno.EPERM);
+            errorPatterns.put(Pattern.compile("No such file or directory"), Errno.ENOENT);
+            errorPatterns.put(Pattern.compile("Input/output error"), Errno.EIO);
+            errorPatterns.put(Pattern.compile("Not a directory"), Errno.ENOTDIR);
+            errorPatterns.put(Pattern.compile("No space left on device"), Errno.ENOSPC);
+            errorPatterns.put(Pattern.compile("Read-only file system"), Errno.EROFS);
+            errorPatterns.put(Pattern.compile("Too many links"), Errno.EMLINK);
+        }
+
+        void parseError(String errorString) {
+            for (Map.Entry<Pattern, Errno> entry : errorPatterns.entrySet()) {
+                if (entry.getKey().matcher(errorString).find()) {
+                    errno.set(entry.getValue());
+                }
+            }
+        }
+    }
+}
diff --git a/src/main/java/jnr/posix/JavaPOSIX.java b/src/main/java/jnr/posix/JavaPOSIX.java
new file mode 100644
index 0000000..d934ebb
--- /dev/null
+++ b/src/main/java/jnr/posix/JavaPOSIX.java
@@ -0,0 +1,893 @@
+package jnr.posix;
+
+import jnr.constants.platform.Errno;
+import jnr.constants.platform.Fcntl;
+import jnr.constants.platform.Signal;
+import jnr.constants.platform.Sysconf;
+import jnr.ffi.Pointer;
+import jnr.posix.util.Java5ProcessMaker;
+import jnr.posix.util.MethodName;
+import jnr.posix.util.Platform;
+import jnr.posix.util.ProcessMaker;
+import jnr.posix.util.SunMiscSignal;
+
+import java.io.*;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.Collection;
+import java.util.Map;
+import jnr.constants.platform.Confstr;
+
+import static jnr.constants.platform.Errno.EINVAL;
+import static jnr.constants.platform.Errno.ENOENT;
+import jnr.constants.platform.Pathconf;
+
+final class JavaPOSIX implements POSIX {
+    private final POSIXHandler handler;
+    private final JavaLibCHelper helper;
+
+    JavaPOSIX(POSIXHandler handler) {
+        this.handler = handler;
+        this.helper = new JavaLibCHelper(handler);
+    }
+
+    public ProcessMaker newProcessMaker(String... command) {
+        return new Java5ProcessMaker(handler, command);
+    }
+
+    public ProcessMaker newProcessMaker() {
+        return new Java5ProcessMaker(handler);
+    }
+    
+    public FileStat allocateStat() {
+        return new JavaFileStat(this, handler);
+    }
+
+    public MsgHdr allocateMsgHdr() {
+        handler.unimplementedError(MethodName.getCallerMethodName());
+        return null;
+    }
+
+    public SocketMacros socketMacros() {
+        handler.unimplementedError(MethodName.getCallerMethodName());
+        return null;
+    }
+
+    public int chmod(String filename, int mode) {
+        return helper.chmod(filename, mode);
+    }
+
+    public int fchmod(int fd, int mode) {
+        handler.unimplementedError("No fchmod in Java (yet)");
+        return -1;
+    }
+
+    public int chown(String filename, int user, int group) {
+        return helper.chown(filename, user, group);
+    }
+
+    public int fchown(int fd, int user, int group) {
+        handler.unimplementedError("No fchown in Java (yet)");
+        return -1;
+    }
+
+    public CharSequence crypt(CharSequence key, CharSequence salt) {
+        return helper.crypt(key, salt);
+    }
+
+    public byte[] crypt(byte[] key, byte[] salt) {
+        return helper.crypt(key, salt);
+    }
+
+    public int exec(String path, String... argv) {
+        handler.unimplementedError("No exec in Java (yet)");
+        
+        return -1;
+    }
+
+    public int exec(String path, String[] argv, String[] envp) {
+        handler.unimplementedError("No exec in Java (yet)");
+        
+        return -1;
+    }
+    
+    public int execv(String path, String[] argv) {
+        handler.unimplementedError("No execv in Java (yet)");
+        
+        return -1;
+    }
+    
+    public int execve(String path, String[] argv, String[] envp) {
+        handler.unimplementedError("No execve in Java (yet)");
+        
+        return -1;
+    }
+    
+    public FileStat fstat(FileDescriptor descriptor) {
+        handler.unimplementedError("fstat unimplemented");
+        
+        return null;
+    }
+
+    public FileStat fstat(int descriptor) {
+        handler.unimplementedError("fstat unimplemented");
+        
+        return null;
+    }
+
+    public int fstat(int fd, FileStat stat) {
+        handler.unimplementedError("fstat unimplemented");
+        return -1;
+    }
+
+    public int fstat(FileDescriptor descriptor, FileStat stat) {
+        handler.unimplementedError("fstat unimplemented");
+        return -1;
+    }
+
+    public int getegid() {
+        return LoginInfo.GID;
+    }
+    
+    public int geteuid() {
+        return LoginInfo.UID;
+    }
+    
+    public int getgid() {
+        return LoginInfo.GID;
+    }
+
+    public int getdtablesize() {
+        handler.unimplementedError("getdtablesize unimplemented");
+        return -1;
+    }
+
+    public String getlogin() {
+        return helper.getlogin();
+    }
+
+    public int getpgid() {
+        return unimplementedInt("getpgid");
+    }
+
+    public int getpgrp() {
+        return unimplementedInt("getpgrp");
+    }
+
+    public int getpid() {
+        return helper.getpid();
+    }
+
+    public int getppid() {
+        return unimplementedInt("getppid");
+    }
+
+    public Passwd getpwent() {
+        return helper.getpwent();
+    }
+
+    public Passwd getpwuid(int which) {
+        return helper.getpwuid(which);
+    }
+
+    public Group getgrgid(int which) {
+        handler.unimplementedError("getgrgid unimplemented");
+        return null;
+    }
+
+    public Passwd getpwnam(String which) {
+        handler.unimplementedError("getpwnam unimplemented");
+        return null;
+    }
+    public Group getgrnam(String which) {
+        handler.unimplementedError("getgrnam unimplemented");
+        return null;
+    }
+
+    public Group getgrent() {
+        handler.unimplementedError("getgrent unimplemented");
+        return null;
+    }
+
+    public int setpwent() {
+        return helper.setpwent();
+    }
+
+    public int endpwent() {
+        return helper.endpwent();
+    }
+
+    public int setgrent() {
+        return unimplementedInt("setgrent");
+    }
+
+    public int endgrent() {
+        return unimplementedInt("endgrent");
+    }
+
+    public Pointer environ() {
+        handler.unimplementedError("environ");
+
+        return null;
+    }
+
+    // @see setenv for more on the environment methods
+    public String getenv(String envName) {
+        return helper.getEnv().get(envName);
+    }
+
+    public int getuid() {
+        return LoginInfo.UID;
+    }
+
+    public int getrlimit(int resource, RLimit rlim) {
+        return unimplementedInt("getrlimit");
+    }
+
+    public int getrlimit(int resource, Pointer rlim) {
+        return unimplementedInt("getrlimit");
+    }
+
+    public RLimit getrlimit(int resource) {
+        handler.unimplementedError("getrlimit");
+
+        return null;
+    }
+
+    public int setrlimit(int resource, RLimit rlim) {
+        return unimplementedInt("setrlimit");
+    }
+
+    public int setrlimit(int resource, Pointer rlim) {
+        return unimplementedInt("setrlimit");
+    }
+
+    public int setrlimit(int resource, long rlimCur, long rlimMax) {
+        return unimplementedInt("setrlimit");
+    }
+
+    public int fork() {
+        return -1;
+    }
+
+    public boolean isatty(FileDescriptor fd) {
+        return (fd == FileDescriptor.in
+                || fd == FileDescriptor.out
+                || fd == FileDescriptor.err);
+    }
+
+    public int isatty(int fd) {
+        return (fd == 0
+                || fd == 1
+                || fd == 2) ? 1 : 0;
+    }
+
+    public int kill(int pid, int signal) {
+        return unimplementedInt("kill");    // FIXME: Can be implemented
+    }
+
+    public int kill(long pid, int signal) {
+        return unimplementedInt("kill");    // FIXME: Can be implemented
+    }
+
+    public SignalHandler signal(Signal sig, SignalHandler handler) {
+        return SunMiscSignal.signal(sig, handler);
+    }
+
+    public int raise(int sig) {
+        return unimplementedInt("raise");
+    }
+
+    public int lchmod(String filename, int mode) {
+        return unimplementedInt("lchmod");    // FIXME: Can be implemented
+    }
+
+    public int lchown(String filename, int user, int group) {
+        return unimplementedInt("lchown");     // FIXME: Can be implemented
+    }
+
+    public int link(String oldpath, String newpath) {
+        return helper.link(oldpath, newpath);
+    }
+
+    public FileStat lstat(String path) {
+        FileStat stat = allocateStat();
+
+        if (lstat(path, stat) < 0) handler.error(ENOENT, "lstat", path);
+        
+        return stat;
+    }
+
+    public int lstat(String path, FileStat stat) {
+        return helper.lstat(path, stat);
+    }
+
+    public int mkdir(String path, int mode) {
+        return helper.mkdir(path, mode);
+    }
+
+    public int rmdir(String path) {
+        return helper.rmdir(path);
+    }
+
+    public String readlink(String path) throws IOException {
+        // TODO: this should not be hardcoded to 256 bytes
+        ByteBuffer buffer = ByteBuffer.allocateDirect(256);
+        int result = helper.readlink(path, buffer, buffer.capacity());
+        
+        if (result == -1) return null;
+        
+        buffer.position(0);
+        buffer.limit(result);
+        return Charset.forName("ASCII").decode(buffer).toString();
+    }
+
+    public int readlink(CharSequence path, byte[] buf, int bufsize) {
+        handler.unimplementedError("readlink");
+
+        return -1;
+    }
+
+    public int readlink(CharSequence path, ByteBuffer buf, int bufsize) {
+        handler.unimplementedError("readlink");
+
+        return -1;
+    }
+
+    public int readlink(CharSequence path, Pointer bufPtr, int bufsize) {
+        handler.unimplementedError("readlink");
+
+        return -1;
+    }
+
+    // At this point the environment is not being used by any methods here.
+    // getenv/setenv/unsetenv do behave properly via POSIX definitions, but 
+    // it is only a storage facility at the moment.  In a future release, this
+    // map will be hooked up to the methods which depend on env.
+    public int setenv(String envName, String envValue, int overwrite) {
+        Map<String, String> env = helper.getEnv();
+        
+        if (envName.contains("=")) {
+            handler.error(EINVAL, "setenv", envName);
+            return -1;
+        }
+        
+        // POSIX specified.  Existence is success if overwrite is 0.
+        if (overwrite == 0 && env.containsKey(envName)) return 0;
+        
+        env.put(envName, envValue);
+        
+        return 0;
+    }
+
+    public FileStat stat(String path) {
+        FileStat stat = allocateStat(); 
+
+        if (helper.stat(path, stat) < 0) handler.error(ENOENT, "stat", path);
+        
+        return stat;
+    }
+
+    public int stat(String path, FileStat stat) {
+        return helper.stat(path, stat);
+    }
+
+    public int symlink(String oldpath, String newpath) {
+        return helper.symlink(oldpath, newpath);
+    }
+
+    public int setegid(int egid) {
+        return unimplementedInt("setegid");
+    }
+
+    public int seteuid(int euid) {
+        return unimplementedInt("seteuid");
+    }
+
+    public int setgid(int gid) {
+        return unimplementedInt("setgid");
+    }
+
+    public int getpgid(int pid) {
+        return unimplementedInt("getpgid");
+    }
+
+    public int setpgid(int pid, int pgid) {
+        return unimplementedInt("setpgid");
+    }
+
+    public int setpgrp(int pid, int pgrp) {
+        return unimplementedInt("setpgrp");
+    }
+
+    public int setsid() {
+        return unimplementedInt("setsid");
+    }
+
+    public int setuid(int uid) {
+        return unimplementedInt("setuid");
+    }
+
+    public int umask(int mask) {
+        // TODO: We can possibly maintain an internal mask and try and apply it to individual
+        // libc methods.  
+        return 0;
+    }
+
+    public int unsetenv(String envName) {
+        if (helper.getEnv().remove(envName) == null) {
+            handler.error(EINVAL, "unsetenv", envName);
+            return -1;
+        }
+        
+        return 0;
+    }
+    
+    public int utimes(String path, long[] atimeval, long[] mtimeval) {
+        long mtimeMillis;
+        if (mtimeval != null) {
+            assert mtimeval.length == 2;
+            mtimeMillis = (mtimeval[0] * 1000) + (mtimeval[1] / 1000);
+        } else {
+            mtimeMillis = System.currentTimeMillis();
+        }
+        new File(path).setLastModified(mtimeMillis);
+        return 0;
+    }
+
+    public int utimes(String path, Pointer times) {
+        return unimplementedInt("utimes");
+    }
+
+    public int futimes(int fd, long[] atimeval, long[] mtimeval) {
+        handler.unimplementedError("futimes");
+        return unimplementedInt("futimes");
+    }
+
+    public int lutimes(String path, long[] atimeval, long[] mtimeval) {
+        handler.unimplementedError("lutimes");
+        return unimplementedInt("lutimes");
+    }
+
+    public int utimensat(int dirfd, String path, long[] atimespec, long[] mtimespec, int flag) {
+        long mtimeMillis;
+        if (mtimespec != null) {
+            assert mtimespec.length == 2;
+            mtimeMillis = (mtimespec[0] * 1000) + (mtimespec[1] / 1000000);
+        } else {
+            mtimeMillis = System.currentTimeMillis();
+        }
+        new File(path).setLastModified(mtimeMillis);
+        return 0;
+    }
+
+    public int utimensat(int dirfd, String path, Pointer times, int flag) {
+        return unimplementedInt("utimensat");
+    }
+
+    public int futimens(int fd, long[] atimespec, long[] mtimespec) {
+        handler.unimplementedError("futimens");
+        return unimplementedInt("futimens");
+    }
+
+    public int futimens(int fd, Pointer times) {
+        handler.unimplementedError("futimens");
+        return unimplementedInt("futimens");
+    }
+
+    public int wait(int[] status) {
+        return unimplementedInt("wait");
+    }
+    
+    public int waitpid(int pid, int[] status, int flags) {
+        return unimplementedInt("waitpid");
+    }
+
+    public int waitpid(long pid, int[] status, int flags) {
+        return unimplementedInt("waitpid");
+    }
+
+    public int getpriority(int which, int who) {
+        return unimplementedInt("getpriority");
+    }
+    
+    public int setpriority(int which, int who, int prio) {
+        return unimplementedInt("setpriority");
+    }
+
+    public long posix_spawnp(String path, Collection<? extends SpawnFileAction> fileActions, Collection<? extends CharSequence> argv, Collection<? extends CharSequence> envp) {
+        return unimplementedInt("posix_spawnp");
+    }
+
+    public long posix_spawnp(String path, Collection<? extends SpawnFileAction> fileActions,
+                             Collection<? extends SpawnAttribute> spawnAttributes,
+                             Collection<? extends CharSequence> argv, Collection<? extends CharSequence> envp) {
+        return unimplementedInt("posix_spawnp");
+    }
+
+    public int errno() {
+        return JavaLibCHelper.errno();
+    }
+
+    public void errno(int value) {
+        JavaLibCHelper.errno(value);
+    }
+    
+    public int chdir(String path) {
+        return JavaLibCHelper.chdir(path);
+    }
+
+    public boolean isNative() {
+        return false;
+    }
+
+    public LibC libc() {
+        return null;
+    }
+
+    private int unimplementedInt(String message) {
+        handler.unimplementedError(message);
+
+        return -1;
+    }
+
+    public long sysconf(Sysconf name) {
+        switch (name) {
+            case _SC_CLK_TCK:
+                return JavaTimes.HZ;
+
+            default:
+                errno(Errno.EOPNOTSUPP.intValue());
+                return -1;
+        }
+    }
+
+    public int confstr(Confstr name, ByteBuffer buf, int len) {
+        errno(Errno.EOPNOTSUPP.intValue());
+        return -1;
+    }
+
+    public int fpathconf(int fd, Pathconf name) {
+        errno(Errno.EOPNOTSUPP.intValue());
+        return -1;
+    }
+
+    public Times times() {
+        return new JavaTimes();
+    }
+    
+    public int flock(int fd, int mode) {
+        return unimplementedInt("flock");
+    }
+
+    public int dup(int fd) {
+        return unimplementedInt("dup");
+    }
+
+    public int dup2(int oldFd, int newFd) {
+        return unimplementedInt("dup2");
+    }
+
+    public int fcntlInt(int fd, Fcntl fcntlConst, int arg) {
+        return unimplementedInt("fcntl");
+    }
+
+    public int fcntl(int fd, Fcntl fcntlConst) {
+        return unimplementedInt("fcntl");
+    }
+
+    public int fcntl(int fd, Fcntl fcntlConst, int arg) {
+        return unimplementedInt("fcntl");
+    }
+
+    @Deprecated
+    public int fcntl(int fd, Fcntl fcntlConst, int... arg) {
+        return unimplementedInt("fcntl");
+    }
+
+    public int access(CharSequence path, int amode) {
+        handler.unimplementedError("access");
+
+        return -1;
+    }
+
+    public int close(int fd) {
+        return unimplementedInt("close");
+    }
+
+    public int unlink(CharSequence path) {
+        handler.unimplementedError("unlink");
+
+        return -1;
+    }
+
+    public int open(CharSequence path, int flags, int perm) {
+        handler.unimplementedError("open");
+
+        return -1;
+    }
+
+
+    public long read(int fd, byte[] buf, long n) {
+        handler.unimplementedError("read");
+
+        return -1;
+    }
+    public long write(int fd, byte[] buf, long n) {
+        handler.unimplementedError("write");
+
+        return -1;
+    }
+    public long read(int fd, ByteBuffer buf, long n) {
+        handler.unimplementedError("read");
+
+        return -1;
+    }
+    public long write(int fd, ByteBuffer buf, long n) {
+        handler.unimplementedError("write");
+
+        return -1;
+    }
+    public long pread(int fd, byte[] buf, long n, long offset) {
+        handler.unimplementedError("pread");
+
+        return -1;
+    }
+    public long pwrite(int fd, byte[] buf, long n, long offset) {
+        handler.unimplementedError("pwrite");
+
+        return -1;
+    }
+    public long pread(int fd, ByteBuffer buf, long n, long offset) {
+        handler.unimplementedError("pread");
+
+        return -1;
+    }
+    public long pwrite(int fd, ByteBuffer buf, long n, long offset) {
+        handler.unimplementedError("pwrite");
+
+        return -1;
+    }
+
+    public int read(int fd, byte[] buf, int n) {
+        handler.unimplementedError("read");
+
+        return -1;
+    }
+    public int write(int fd, byte[] buf, int n) {
+        handler.unimplementedError("write");
+
+        return -1;
+    }
+    public int read(int fd, ByteBuffer buf, int n) {
+        handler.unimplementedError("read");
+
+        return -1;
+    }
+    public int write(int fd, ByteBuffer buf, int n) {
+        handler.unimplementedError("write");
+
+        return -1;
+    }
+    public int pread(int fd, byte[] buf, int n, int offset) {
+        handler.unimplementedError("pread");
+
+        return -1;
+    }
+    public int pwrite(int fd, byte[] buf, int n, int offset) {
+        handler.unimplementedError("pwrite");
+
+        return -1;
+    }
+    public int pread(int fd, ByteBuffer buf, int n, int offset) {
+        handler.unimplementedError("pread");
+
+        return -1;
+    }
+    public int pwrite(int fd, ByteBuffer buf, int n, int offset) {
+        handler.unimplementedError("pwrite");
+
+        return -1;
+    }
+
+    public int lseek(int fd, long offset, int whence) {
+        handler.unimplementedError("lseek");
+
+        return -1;
+    }
+
+    public long lseekLong(int fd, long offset, int whence) {
+        handler.unimplementedError("lseek");
+
+        return -1;
+    }
+
+    public int pipe(int[] fds) {
+        handler.unimplementedError("pipe");
+
+        return -1;
+    }
+
+    public int socketpair(int domain, int type, int protocol, int[] fds) {
+        handler.unimplementedError("socketpair");
+
+        return -1;
+    }
+
+    public int sendmsg(int socket, MsgHdr message, int flags) {
+        handler.unimplementedError("sendmsg");
+
+        return -1;
+    }
+
+    public int recvmsg(int socket, MsgHdr message, int flags) {
+        handler.unimplementedError("recvmsg");
+
+        return -1;
+    }
+
+    public int truncate(CharSequence path, long length) {
+        handler.unimplementedError("truncate");
+
+        return -1;
+    }
+
+    public int ftruncate(int fd, long offset) {
+        handler.unimplementedError("ftruncate");
+
+        return -1;
+    }
+
+    public int rename(CharSequence oldName, CharSequence newName) {
+        // Very basic support.  This might not work well with rename's semantics regarding symlinks.
+
+        File oldFile = new File(oldName.toString());
+        File newFile = new File(newName.toString());
+
+        if (oldFile.renameTo(newFile)) {
+            return 0;
+        }
+
+        return -1;
+    }
+
+    public String getcwd() {
+        return System.getProperty("user.dir");
+    }
+
+    public int fsync(int fd) {
+        handler.unimplementedError("fsync");
+        return unimplementedInt("fsync not available for Java");
+    }
+
+    public int fdatasync(int fd) {
+        handler.unimplementedError("fdatasync");
+        return unimplementedInt("fdatasync not available for Java");
+    }
+
+    public int mkfifo(String filename, int mode) {
+        handler.unimplementedError("mkfifo");
+        return unimplementedInt("mkfifo not available for Java");
+    }
+
+    public int daemon(int nochdir, int noclose) {
+        handler.unimplementedError("daemon");
+        return unimplementedInt("daemon not available for Java");
+    }
+
+    public long[] getgroups() {
+        handler.unimplementedError("getgroups");
+        return null;
+    }
+
+    public int getgroups(int size, int[] groups) {
+        handler.unimplementedError("getgroups");
+        return unimplementedInt("getgroups not available for Java");
+    }
+
+    public String nl_langinfo(int item) {
+        handler.unimplementedError("nl_langinfo");
+        return null;
+    }
+
+    public String setlocale(int category, String locale) {
+        handler.unimplementedError("setlocale");
+        return null;
+    }
+
+    @Override
+    public String strerror(int code) {
+        handler.unimplementedError("strerror");
+        return null;
+    }
+
+    public Timeval allocateTimeval() {
+        handler.unimplementedError("allocateTimeval");
+        return null;
+    }
+
+    @Override
+    public int gettimeofday(Timeval tv) {
+        handler.unimplementedError("gettimeofday");
+        return -1;
+    }
+
+    public String gethostname() {
+        return helper.gethostname();
+    }
+
+    static final class LoginInfo {
+        public static final int UID = IDHelper.getInt("-u");
+        public static final int GID = IDHelper.getInt("-g");
+        public static final String USERNAME = IDHelper.getString("-un");
+    }
+    private static final class IDHelper {
+        private static final String ID_CMD = Platform.IS_SOLARIS ? "/usr/xpg4/bin/id" : "/usr/bin/id";
+        private static final int NOBODY = Platform.IS_WINDOWS ? 0 : Short.MAX_VALUE;
+        public static int getInt(String option) {
+            try {
+                Process p = Runtime.getRuntime().exec(new String[] { ID_CMD, option });
+                BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()));
+                return Integer.parseInt(r.readLine());
+            } catch (IOException ex) {
+                return NOBODY;
+            } catch (NumberFormatException ex) {
+                return NOBODY;
+            } catch (SecurityException ex) {
+                return NOBODY;
+            }
+        }
+        public static String getString(String option) {
+            try {
+                Process p = Runtime.getRuntime().exec(new String[] { ID_CMD, option });
+                BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()));
+                return r.readLine();
+            } catch (IOException ex) {
+                return null;
+            }
+        }
+    }
+    private static final class FakePasswd implements Passwd {
+
+        public String getLoginName() {
+            return LoginInfo.USERNAME;
+        }
+
+        public String getPassword() {
+            return "";
+        }
+
+        public long getUID() {
+            return LoginInfo.UID;
+        }
+
+        public long getGID() {
+            return LoginInfo.GID;
+        }
+
+        public int getPasswdChangeTime() {
+            return 0;
+        }
+
+        public String getAccessClass() {
+            return "";
+        }
+
+        public String getGECOS() {
+            return getLoginName();
+        }
+
+        public String getHome() {
+            return "/";
+        }
+
+        public String getShell() {
+            return "/bin/sh";
+        }
+
+        public int getExpire() {
+            return ~0;
+        }
+
+    }
+}
diff --git a/src/main/java/jnr/posix/JavaPasswd.java b/src/main/java/jnr/posix/JavaPasswd.java
new file mode 100644
index 0000000..cfd37b3
--- /dev/null
+++ b/src/main/java/jnr/posix/JavaPasswd.java
@@ -0,0 +1,59 @@
+package jnr.posix;
+
+final class JavaPasswd implements Passwd {
+    private final POSIXHandler handler;
+
+    public JavaPasswd(POSIXHandler handler) {
+        this.handler = handler;
+    }
+    
+    public String getAccessClass() {
+        handler.unimplementedError("passwd.pw_access unimplemented");
+        
+        return null;
+    }
+
+    public String getGECOS() {
+        return getLoginName();
+    }
+
+    public long getGID() {
+        return JavaPOSIX.LoginInfo.GID;
+    }
+
+    public String getHome() {
+        return System.getProperty("user.home");
+    }
+
+    public String getLoginName() {
+        return System.getProperty("user.name");
+    }
+
+    public int getPasswdChangeTime() {
+        handler.unimplementedError("passwd.pw_change unimplemented");
+
+        return 0;
+    }
+
+    public String getPassword() {
+        handler.unimplementedError("passwd.pw_passwd unimplemented");
+        
+        return null;
+    }
+
+    public String getShell() {
+        handler.unimplementedError("passwd.pw_env unimplemented");
+        
+        return null;
+    }
+
+    public long getUID() {
+        return JavaPOSIX.LoginInfo.UID;
+    }
+
+    public int getExpire() {
+        handler.unimplementedError("passwd.expire unimplemented");
+        
+        return ~0;
+    }
+}
diff --git a/src/main/java/jnr/posix/JavaSecuredFile.java b/src/main/java/jnr/posix/JavaSecuredFile.java
new file mode 100644
index 0000000..b7d9295
--- /dev/null
+++ b/src/main/java/jnr/posix/JavaSecuredFile.java
@@ -0,0 +1,256 @@
+/**
+ * ** BEGIN LICENSE BLOCK *****
+ * Version: EPL 2.0/GPL 2.0/LGPL 2.1
+ * <p/>
+ * The contents of this file are subject to the Eclipse Public
+ * License Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ * <p/>
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ * <p/>
+ * <p/>
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ * **** END LICENSE BLOCK ****
+ */
+
+package jnr.posix;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.net.URI;
+
+@SuppressWarnings("serial")
+public class JavaSecuredFile extends File {
+
+    public JavaSecuredFile(String pathname) {
+        super(pathname);
+    }
+
+    public JavaSecuredFile(String parent, String child) {
+        super(parent, child);
+    }
+
+    public JavaSecuredFile(File parent, String child) {
+        super(parent, child);
+    }
+
+    public JavaSecuredFile(URI uri) {
+        super(uri);
+    }
+
+
+    @Override
+    public File getParentFile() {
+        String path = getParent();
+        return path == null ? null : new JavaSecuredFile(path);
+    }
+
+    @Override
+    public File getAbsoluteFile() {
+        String path = getAbsolutePath();
+        return path == null ? null : new JavaSecuredFile(path);
+    }
+
+    @Override
+    public File getCanonicalFile() throws IOException {
+        String path = getCanonicalPath();
+        return path == null ? null : new JavaSecuredFile(path);
+    }
+
+    @Override
+    public boolean canRead() {
+        try {
+            return super.canRead();
+        } catch (SecurityException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean canWrite() {
+        try {
+            return super.canWrite();
+        } catch (SecurityException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean exists() {
+        try {
+            return super.exists();
+        } catch (SecurityException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean isDirectory() {
+        try {
+            return super.isDirectory();
+        } catch (SecurityException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean isFile() {
+        try {
+            return super.isFile();
+        } catch (SecurityException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean isHidden() {
+        try {
+            return super.isHidden();
+        } catch (SecurityException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean delete() {
+        try {
+            return super.delete();
+        } catch (SecurityException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean mkdir() {
+        try {
+            return super.mkdir();
+        } catch (SecurityException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean mkdirs() {
+        try {
+            return super.mkdirs();
+        } catch (SecurityException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean renameTo(File dest) {
+        try {
+            return super.renameTo(dest);
+        } catch (SecurityException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean setLastModified(long time) {
+        try {
+            return super.setLastModified(time);
+        } catch (SecurityException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean setReadOnly() {
+        try {
+            return super.setReadOnly();
+        } catch (SecurityException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public String getCanonicalPath() throws IOException {
+        try {
+            return super.getCanonicalPath();
+        } catch (SecurityException e) {
+            throw new IOException(e);
+        }
+    }
+
+    @Override
+    public boolean createNewFile() throws IOException {
+        try {
+            return super.createNewFile();
+        } catch (SecurityException e) {
+            throw new IOException(e);
+        }
+    }
+
+    @Override
+    public String[] list() {
+        try {
+            return super.list();
+        } catch (SecurityException e) {
+            return null;
+        }
+    }
+
+    @Override
+    public String[] list(FilenameFilter filter) {
+        try {
+            return super.list(filter);
+        } catch (SecurityException e) {
+            return null;
+        }
+    }
+
+    @Override
+    public File[] listFiles() {
+        try {
+            return super.listFiles();
+        } catch (SecurityException e) {
+            return null;
+        }
+    }
+
+    @Override
+    public File[] listFiles(FileFilter filter) {
+        try {
+            return super.listFiles(filter);
+        } catch (SecurityException e) {
+            return null;
+        }
+    }
+
+    @Override
+    public long lastModified() {
+        try {
+            return super.lastModified();
+        } catch (SecurityException e) {
+            return 0L;
+        }
+    }
+
+    @Override
+    public long length() {
+        try {
+            return super.length();
+        } catch (SecurityException e) {
+            return 0L;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/jnr/posix/JavaTimes.java b/src/main/java/jnr/posix/JavaTimes.java
new file mode 100644
index 0000000..44cb0d1
--- /dev/null
+++ b/src/main/java/jnr/posix/JavaTimes.java
@@ -0,0 +1,22 @@
+package jnr.posix;
+
+final class JavaTimes implements Times {
+    private static final long startTime = System.currentTimeMillis();
+    static final long HZ = 1000;
+
+    public long utime() {
+        return Math.max(System.currentTimeMillis() - startTime, 1);
+    }
+
+    public long stime() {
+        return 0;
+    }
+
+    public long cutime() {
+        return 0;
+    }
+
+    public long cstime() {
+        return 0;
+    }
+}
diff --git a/src/main/java/jnr/posix/LazyPOSIX.java b/src/main/java/jnr/posix/LazyPOSIX.java
new file mode 100644
index 0000000..14d5213
--- /dev/null
+++ b/src/main/java/jnr/posix/LazyPOSIX.java
@@ -0,0 +1,635 @@
+
+package jnr.posix;
+
+import jnr.constants.platform.Fcntl;
+import jnr.constants.platform.Sysconf;
+import jnr.ffi.Pointer;
+import jnr.posix.util.ProcessMaker;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Collection;
+import jnr.constants.platform.Confstr;
+import jnr.constants.platform.Pathconf;
+import jnr.constants.platform.Signal;
+
+final class LazyPOSIX implements POSIX {
+
+    private final POSIXHandler handler;
+    private final boolean useNativePosix;
+
+    // NOTE: because all implementations of POSIX that are loaded via loadPOSIX()
+    // are immutable, there is no need for 'posix' to be a volatile field.
+    // See http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
+    // (but since volatile reads on x86 and x86_64 are cheap, do it anyway)
+    private volatile POSIX posix;
+
+    LazyPOSIX(POSIXHandler handler, boolean useNativePosix) {
+        this.handler = handler;
+        this.useNativePosix = useNativePosix;
+    }
+
+    private final POSIX posix() {
+        return posix != null ? posix : loadPOSIX();
+    }
+
+    private final synchronized POSIX loadPOSIX() {
+        return posix != null
+                ? posix
+                : (posix = POSIXFactory.loadPOSIX(handler, useNativePosix));
+    }
+
+    public ProcessMaker newProcessMaker(String... command) {
+        return posix().newProcessMaker(command);
+    }
+
+    public ProcessMaker newProcessMaker() {
+        return posix().newProcessMaker();
+    }
+    
+    public FileStat allocateStat() {
+        return posix().allocateStat();
+    }
+
+    public MsgHdr allocateMsgHdr() {
+        return posix().allocateMsgHdr();
+    }
+
+    public int chdir(String path) {
+        return posix().chdir(path);
+    }
+
+    public int chmod(String filename, int mode) {
+        return posix().chmod(filename, mode);
+    }
+
+    public int fchmod(int fd, int mode) {
+        return posix().fchmod(fd, mode);
+    }
+
+    public int chown(String filename, int user, int group) {
+        return posix().chown(filename, user, group);
+    }
+
+    public CharSequence crypt(CharSequence key, CharSequence salt) {
+        return posix().crypt(key, salt);
+    }
+
+    public byte[] crypt(byte[] key, byte[] salt) {
+        return posix().crypt(key, salt);
+    }
+
+    public int fchown(int fd, int user, int group) {
+        return posix().fchown(fd, user, group);
+    }
+
+    public int endgrent() {
+        return posix().endgrent();
+    }
+
+    public int endpwent() {
+        return posix().endpwent();
+    }
+
+    public int errno() {
+        return posix().errno();
+    }
+
+    public void errno(int value) {
+        posix().errno(value);
+    }
+    
+    public int exec(String path, String... args) {
+        return posix().exec(path, args);
+    }
+    
+    public int exec(String path, String[] args, String[] envp) {
+        return posix().exec(path, args, envp);
+    }
+
+    public int execv(String path, String[] argv) {
+        return posix().execv(path, argv);
+    }
+
+    public int execve(String path, String[] argv, String[] envp) {
+        return posix().execve(path, argv, envp);
+    }
+    
+    public int fork() {
+        return posix().fork();
+    }
+
+    public FileStat fstat(int fd) {
+        return posix().fstat(fd);
+    }
+
+    public int fstat(int fd, FileStat stat) {
+        return posix().fstat(fd, stat);
+    }
+
+    public FileStat fstat(FileDescriptor descriptor) {
+        return posix().fstat(descriptor);
+    }
+
+    public int fstat(FileDescriptor descriptor, FileStat stat) {
+        return posix().fstat(descriptor, stat);
+    }
+
+    public int getegid() {
+        return posix().getegid();
+    }
+
+    public int geteuid() {
+        return posix().geteuid();
+    }
+
+    public int getgid() {
+        return posix().getgid();
+    }
+
+    public int getdtablesize() {
+        return posix().getdtablesize();
+    }
+
+    public Group getgrent() {
+        return posix().getgrent();
+    }
+
+    public Group getgrgid(int which) {
+        return posix().getgrgid(which);
+    }
+
+    public Group getgrnam(String which) {
+        return posix().getgrnam(which);
+    }
+
+    public String getlogin() {
+        return posix().getlogin();
+    }
+
+    public int getpgid() {
+        return posix().getpgid();
+    }
+
+    public int getpgid(int pid) {
+        return posix().getpgid(pid);
+    }
+
+    public int getpgrp() {
+        return posix().getpgrp();
+    }
+
+    public int getpid() {
+        return posix().getpid();
+    }
+
+    public int getppid() {
+        return posix().getppid();
+    }
+
+    public int getpriority(int which, int who) {
+        return posix().getpriority(which, who);
+    }
+
+    public Passwd getpwent() {
+        return posix().getpwent();
+    }
+
+    public Passwd getpwnam(String which) {
+        return posix().getpwnam(which);
+    }
+
+    public Passwd getpwuid(int which) {
+        return posix().getpwuid(which);
+    }
+
+    public int getuid() {
+        return posix().getuid();
+    }
+
+    public int getrlimit(int resource, RLimit rlim) {
+        return posix().getrlimit(resource, rlim);
+    }
+
+    public int getrlimit(int resource, Pointer rlim) {
+        return posix().getrlimit(resource, rlim);
+    }
+
+    public RLimit getrlimit(int resource) {
+        return posix().getrlimit(resource);
+    }
+
+    public int setrlimit(int resource, RLimit rlim) {
+        return posix().setrlimit(resource, rlim);
+    }
+
+    public int setrlimit(int resource, Pointer rlim) {
+        return posix().setrlimit(resource, rlim);
+    }
+
+    public int setrlimit(int resource, long rlimCur, long rlimMax) {
+        return posix().setrlimit(resource, rlimCur, rlimMax);
+    }
+
+    public boolean isatty(FileDescriptor descriptor) {
+        return posix().isatty(descriptor);
+    }
+
+    public int isatty(int descriptor) {
+        return posix().isatty(descriptor);
+    }
+
+    public int kill(int pid, int signal) {
+        return kill((long) pid, signal);
+    }
+
+    public int kill(long pid, int signal) {
+        return posix().kill(pid, signal);
+    }
+    
+    public SignalHandler signal(Signal sig, SignalHandler handler) {
+        return posix().signal(sig, handler);
+    }
+
+    public int raise(int sig) {
+        return posix().raise(sig);
+    }
+
+    public int lchmod(String filename, int mode) {
+        return posix().lchmod(filename, mode);
+    }
+
+    public int lchown(String filename, int user, int group) {
+        return posix().lchown(filename, user, group);
+    }
+
+    public int link(String oldpath, String newpath) {
+        return posix().link(oldpath, newpath);
+    }
+
+    public FileStat lstat(String path) {
+        return posix().lstat(path);
+    }
+
+    public int lstat(String path, FileStat stat) {
+        return posix().lstat(path, stat);
+    }
+
+    public int mkdir(String path, int mode) {
+        return posix().mkdir(path, mode);
+    }
+
+    public String readlink(String path) throws IOException {
+        return posix().readlink(path);
+    }
+
+    public int readlink(CharSequence path, byte[] buf, int bufsize) {
+        return posix().readlink(path, buf, bufsize);
+    }
+
+    public int readlink(CharSequence path, ByteBuffer buf, int bufsize) {
+        return posix().readlink(path, buf, bufsize);
+    }
+
+    public int readlink(CharSequence path, Pointer bufPtr, int bufsize) {
+        return posix().readlink(path, bufPtr, bufsize);
+    }
+
+    public int rmdir(String path) {
+        return posix().rmdir(path);
+    }
+
+    public int setegid(int egid) {
+        return posix().setegid(egid);
+    }
+
+    public int seteuid(int euid) {
+        return posix().seteuid(euid);
+    }
+
+    public int setgid(int gid) {
+        return posix().setgid(gid);
+    }
+
+    public int setgrent() {
+        return posix().setgrent();
+    }
+
+    public int setpgid(int pid, int pgid) {
+        return posix().setpgid(pid, pgid);
+    }
+
+    public int setpgrp(int pid, int pgrp) {
+        return posix().setpgrp(pid, pgrp);
+    }
+
+    public int setpriority(int which, int who, int prio) {
+        return posix().setpriority(which, who, prio);
+    }
+
+    public int setpwent() {
+        return posix().setpwent();
+    }
+
+    public int setsid() {
+        return posix().setsid();
+    }
+
+    public int setuid(int uid) {
+        return posix().setuid(uid);
+    }
+
+    public FileStat stat(String path) {
+        return posix().stat(path);
+    }
+
+    public int stat(String path, FileStat stat) {
+        return posix().stat(path, stat);
+    }
+
+    public int symlink(String oldpath, String newpath) {
+        return posix().symlink(oldpath, newpath);
+    }
+
+    public int umask(int mask) {
+        return posix().umask(mask);
+    }
+
+    public int utimes(String path, long[] atimeval, long[] mtimeval) {
+        return posix().utimes(path, atimeval, mtimeval);
+    }
+
+    public int utimes(String path, Pointer times) {
+        return posix().utimes(path, times);
+    }
+
+    public int futimes(int fd, long[] atimeval, long[] mtimeval) {
+        return posix().futimes(fd, atimeval, mtimeval);
+    }
+
+    public int lutimes(String path, long[] atimeval, long[] mtimeval) {
+        return posix().lutimes(path, atimeval, mtimeval);
+    }
+
+    public int utimensat(int dirfd, String path, long[] atimespec, long[] mtimespec, int flag) {
+        return posix().utimensat(dirfd, path, atimespec, mtimespec, flag);
+    }
+
+    public int utimensat(int dirfd, String path, Pointer times, int flag) {
+        return posix().utimensat(dirfd, path, times, flag);
+    }
+
+    public int futimens(int fd, long[] atimespec, long[] mtimespec) {
+        return posix().futimens(fd, atimespec, mtimespec);
+    }
+
+    public int futimens(int fd, Pointer times) {
+        return posix().futimens(fd, times);
+    }
+
+    public int wait(int[] status) {
+        return posix().wait(status);
+    }
+
+    public int waitpid(int pid, int[] status, int flags) {
+        return waitpid((long) pid, status, flags);
+    }
+
+    public int waitpid(long pid, int[] status, int flags) {
+        return posix().waitpid(pid, status, flags);
+    }
+
+    public boolean isNative() {
+        return posix().isNative();
+    }
+
+    public LibC libc() {
+        return posix().libc();
+    }
+
+    public Pointer environ() {
+        return posix().environ();
+    }
+
+    public String getenv(String envName) {
+        return posix().getenv(envName);
+    }
+
+    public int setenv(String envName, String envValue, int overwrite) {
+        return posix().setenv(envName, envValue, overwrite);
+    }
+
+    public int unsetenv(String envName) {
+        return posix().unsetenv(envName);
+    }
+
+    public long posix_spawnp(String path, Collection<? extends SpawnFileAction> fileActions, Collection<? extends CharSequence> argv, Collection<? extends CharSequence> envp) {
+        return posix().posix_spawnp(path, fileActions, argv, envp);
+    }
+
+    public long posix_spawnp(String path, Collection<? extends SpawnFileAction> fileActions,
+                             Collection<? extends SpawnAttribute> spawnAttributes,
+                             Collection<? extends CharSequence> argv, Collection<? extends CharSequence> envp) {
+        return posix().posix_spawnp(path, fileActions, spawnAttributes, argv, envp);
+    }
+
+    public long sysconf(Sysconf name) {
+        return posix().sysconf(name);
+    }
+
+    public int confstr(Confstr name, ByteBuffer buf, int len) {
+        return posix().confstr(name, buf, len);
+    }
+
+    public int fpathconf(int fd, Pathconf name) {
+        return posix().fpathconf(fd, name);
+    }
+
+    public Times times() {
+        return posix().times();
+    }
+    
+    public int flock(int fd, int mode) {
+        return posix().flock(fd, mode);
+    }
+
+    public int dup(int fd) {
+        return posix().dup(fd);
+    }
+
+    public int dup2(int oldFd, int newFd) {
+        return posix().dup2(oldFd, newFd);
+    }
+
+    public int fcntlInt(int fd, Fcntl fcntlConst, int arg) {
+        return posix().fcntlInt(fd, fcntlConst, arg);
+    }
+
+    public int fcntl(int fd, Fcntl fcntlConst) {
+        return posix().fcntl(fd, fcntlConst);
+    }
+
+    public int fcntl(int fd, Fcntl fcntlConst, int arg) {
+        return posix().fcntl(fd, fcntlConst, arg);
+    }
+
+    @Deprecated
+    public int fcntl(int fd, Fcntl fcntlConst, int... arg) {
+        return posix().fcntl(fd, fcntlConst);
+    }
+
+    public int access(CharSequence path, int amode) {
+        return posix().access(path, amode);
+    }
+
+    public int close(int fd) {
+        return posix().close(fd);
+    }
+
+    public int unlink(CharSequence path) {
+        return posix().unlink(path);
+    }
+
+    public int open(CharSequence path, int flags, int perm) {
+        return posix().open(path, flags, perm);
+    }
+
+    public long read(int fd, byte[] buf, long n) {
+        return posix().read(fd, buf, n);
+    }
+    public long write(int fd, byte[] buf, long n) {
+        return posix().write(fd, buf, n);
+    }
+    public long read(int fd, ByteBuffer buf, long n) {
+        return posix().read(fd, buf, n);
+    }
+    public long write(int fd, ByteBuffer buf, long n) {
+        return posix().write(fd, buf, n);
+    }
+    public long pread(int fd, byte[] buf, long n, long offset) {
+        return posix().pread(fd, buf, n, offset);
+    }
+    public long pwrite(int fd, byte[] buf, long n, long offset) {
+        return posix().pwrite(fd, buf, n, offset);
+    }
+    public long pread(int fd, ByteBuffer buf, long n, long offset) {
+        return posix().pread(fd, buf, n, offset);
+    }
+    public long pwrite(int fd, ByteBuffer buf, long n, long offset) {
+        return posix().pwrite(fd, buf, n, offset);
+    }
+
+    public int read(int fd, byte[] buf, int n)
+    {
+        return posix().read(fd, buf, n);
+    }
+    public int write(int fd, byte[] buf, int n) {
+        return posix().write(fd, buf, n);
+    }
+    public int read(int fd, ByteBuffer buf, int n) {
+        return posix().read(fd, buf, n);
+    }
+    public int write(int fd, ByteBuffer buf, int n) {
+        return posix().write(fd, buf, n);
+    }
+    public int pread(int fd, byte[] buf, int n, int offset) {
+        return posix().pread(fd, buf, n, offset);
+    }
+    public int pwrite(int fd, byte[] buf, int n, int offset) {
+        return posix().pwrite(fd, buf, n, offset);
+    }
+    public int pread(int fd, ByteBuffer buf, int n, int offset) {
+        return posix().pread(fd, buf, n, offset);
+    }
+    public int pwrite(int fd, ByteBuffer buf, int n, int offset) {
+        return posix().pwrite(fd, buf, n, offset);
+    }
+
+    public int lseek(int fd, long offset, int whence) {
+        return posix().lseek(fd, offset, whence);
+    }
+
+    public long lseekLong(int fd, long offset, int whence) {
+        return posix().lseekLong(fd, offset, whence);
+    }
+
+    public int pipe(int[] fds) {
+        return posix().pipe(fds);
+    }
+
+    public int socketpair(int domain, int type, int protocol, int[] fds) {
+        return posix().socketpair(domain, type, protocol, fds);
+    }
+
+    public int sendmsg(int socket, MsgHdr message, int flags) {
+        return posix().sendmsg(socket, message, flags);
+    }
+
+    public int recvmsg(int socket, MsgHdr message, int flags) {
+        return posix().recvmsg(socket, message, flags);
+    }
+
+    public int truncate(CharSequence path, long length) {
+        return posix().truncate(path, length);
+    }
+
+    public int ftruncate(int fd, long offset) {
+        return posix().ftruncate(fd, offset);
+    }
+
+    public int rename(CharSequence oldName, CharSequence newName) {
+        return posix().rename(oldName, newName);
+    }
+
+    public String getcwd() {
+        return posix().getcwd();
+    }
+
+    public int fsync(int fd) {
+        return posix().fsync(fd);
+    }
+
+    public int fdatasync(int fd) {
+        return posix().fdatasync(fd);
+    }
+
+    public int mkfifo(String path, int mode) {
+        return posix().mkfifo(path, mode);
+    }
+
+    public String gethostname() {
+        return posix().gethostname();
+    }
+
+    public int daemon(int nochdir, int noclose) {
+        return posix().daemon(nochdir, noclose);
+    }
+
+    public long[] getgroups() {
+        return posix().getgroups();
+    }
+
+    public int getgroups(int size, int[] groups) {
+        return posix().getgroups(size, groups);
+    }
+
+    public String nl_langinfo(int item) {
+        return posix().nl_langinfo(item);
+    }
+
+    public String setlocale(int category, String locale) {
+        return posix().setlocale(category, locale);
+    }
+
+    @Override
+    public String strerror(int code) {
+        return posix().strerror(code);
+    }
+
+    @Override
+    public Timeval allocateTimeval() { return posix().allocateTimeval(); }
+
+    @Override
+    public int gettimeofday(Timeval tv) { return posix().gettimeofday(tv); }
+}
diff --git a/src/main/java/jnr/posix/LibC.java b/src/main/java/jnr/posix/LibC.java
new file mode 100644
index 0000000..fb384e7
--- /dev/null
+++ b/src/main/java/jnr/posix/LibC.java
@@ -0,0 +1,207 @@
+/*
+ **** BEGIN LICENSE BLOCK *****
+ * Version: EPL 2.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Eclipse Public
+ * License Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * Copyright (C) 2007 Thomas E Enebo <enebo@acm.org>
+ * Copyright (C) 2007 Charles O Nutter <headius@headius.com>
+ * 
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package jnr.posix;
+
+import jnr.constants.platform.Sysconf;
+import jnr.ffi.Pointer;
+import jnr.ffi.Variable;
+import jnr.ffi.annotations.*;
+import jnr.ffi.types.*;
+
+import java.nio.ByteBuffer;
+import jnr.constants.platform.Confstr;
+import jnr.constants.platform.Pathconf;
+import jnr.ffi.byref.IntByReference;
+
+public interface LibC {
+    int chmod(CharSequence filename, int mode);
+    int fchmod(int fd, int mode);
+    int chown(CharSequence filename, int user, int group);
+    int fchown(int fd, int user, int group);
+    int fstat(int fd, @Out @Transient FileStat stat);
+    int fstat64(int fd, @Out @Transient FileStat stat);
+    String getenv(CharSequence envName);
+    @IgnoreError int getegid();
+    int setegid(int egid);
+    @IgnoreError int geteuid();
+    int seteuid(int euid);
+    @IgnoreError int getgid();
+    String getlogin();
+    int setgid(int gid);
+    int getpgid();
+    int getpgid(int pid);
+    int setpgid(int pid, int pgid);
+    int getpgrp();
+    int setpgrp(int pid, int pgrp);
+    @IgnoreError int getppid();
+    @IgnoreError int getpid();
+    NativePasswd getpwent();
+    NativePasswd getpwuid(int which);
+    NativePasswd getpwnam(CharSequence which);
+    NativeGroup getgrent();
+    NativeGroup getgrgid(int which);
+    NativeGroup getgrnam(CharSequence which);
+    int setpwent();
+    int endpwent();
+    int setgrent();
+    int endgrent();
+    @IgnoreError int getuid();
+    int setsid();
+    int setuid(int uid);
+    int getrlimit(int resource, @Out RLimit rlim);
+    int getrlimit(int resource, Pointer rlim);
+    int setrlimit(int resource, @In RLimit rlim);
+    int setrlimit(int resource, Pointer rlim);
+    int kill(int pid, int signal);
+    int kill(long pid, int signal);
+
+    int dup(int fd);
+    int dup2(int oldFd, int newFd);
+
+    @Variadic(fixedCount = 2)
+    int fcntl(int fd, int fnctl, Flock arg);
+    @Variadic(fixedCount = 2)
+    int fcntl(int fd, int fnctl, Pointer arg);
+    @Variadic(fixedCount = 2)
+    int fcntl(int fd, int fnctl);
+    @Variadic(fixedCount = 2)
+    int fcntl(int fd, int fnctl, @u_int64_t int arg);
+    @Deprecated
+    int fcntl(int fd, int fnctl, int... arg);
+    int access(CharSequence path, int amode);
+    int getdtablesize();
+
+    public interface LibCSignalHandler {
+        @Delegate void signal(int sig);
+    }
+    @intptr_t long signal(int sig, LibCSignalHandler handler);
+    int raise(int raise);
+    int lchmod(CharSequence filename, int mode);
+    int lchown(CharSequence filename, int user, int group);
+    int link(CharSequence oldpath, CharSequence newpath);
+    int lstat(CharSequence path, @Out @Transient FileStat stat);
+    int lstat64(CharSequence path, @Out @Transient FileStat stat);
+    int mkdir(CharSequence path, int mode);
+    int rmdir(CharSequence path);
+    int stat(CharSequence path, @Out @Transient FileStat stat);
+    int stat64(CharSequence path, @Out @Transient FileStat stat);
+    int symlink(CharSequence oldpath, CharSequence newpath);
+    int readlink(CharSequence oldpath, @Out ByteBuffer buffer, int len);
+    int readlink(CharSequence path, @Out byte[] buffer, int len);
+    int readlink(CharSequence path, Pointer bufPtr, int bufsize);
+    int setenv(CharSequence envName, CharSequence envValue, int overwrite);
+    @IgnoreError int umask(int mask);
+    int unsetenv(CharSequence envName);
+    int utimes(CharSequence path, @In Timeval[] times);
+    int utimes(String path, @In Pointer times);
+    int futimes(int fd, @In Timeval[] times);
+    int lutimes(CharSequence path, @In Timeval[] times);
+    int utimensat(int dirfd, String path, Timespec[] times, int flag);
+    int utimensat(int dirfd, String path, @In Pointer times, int flag);
+    int futimens(int fd, Timespec[] times);
+    int futimens(int fd, @In Pointer times);
+    int fork();
+    int waitpid(long pid, @Out int[] status, int options);
+    int wait(@Out int[] status);
+    int getpriority(int which, int who);
+    int setpriority(int which, int who, int prio);
+    @IgnoreError int isatty(int fd);
+
+    @ssize_t long read(int fd, @Out byte[] dst, @size_t long len);
+    @ssize_t long write(int fd, @In byte[] src, @size_t long len);
+    @ssize_t long read(int fd, @Out ByteBuffer dst, @size_t long len);
+    @ssize_t long write(int fd, @In ByteBuffer src, @size_t long len);
+    @ssize_t long pread(int fd, @Out byte[] dst, @size_t long len, @off_t long offset);
+    @ssize_t long pwrite(int fd, @In byte[] src, @size_t long len, @off_t long offset);
+    @ssize_t long pread(int fd, @Out ByteBuffer dst, @size_t long len, @off_t long offset);
+    @ssize_t long pwrite(int fd, @In ByteBuffer src, @size_t long len, @off_t long offset);
+
+    int read(int fd, @Out byte[] dst, int len);
+    int write(int fd, @In byte[] src, int len);
+    int read(int fd, @Out ByteBuffer dst, int len);
+    int write(int fd, @In ByteBuffer src, int len);
+    int pread(int fd, @Out byte[] dst, int len, int offset);
+    int pwrite(int fd, @In byte[] src, int len, int offset);
+    int pread(int fd, @Out ByteBuffer dst, int len, int offset);
+    int pwrite(int fd, @In ByteBuffer src, int len, int offset);
+
+    long lseek(int fd, long offset, int whence);
+    int close(int fd);
+    int execv(CharSequence path, @In CharSequence[] argv);
+    int execve(CharSequence path, @In CharSequence[] argv, @In CharSequence[] envp);
+    int chdir(CharSequence path);
+
+    public long sysconf(Sysconf name);
+
+    public int confstr(Confstr name, @Out ByteBuffer buf, int len);
+
+    public int fpathconf(int fd, Pathconf name);
+
+    public @clock_t long times(@Out @Transient NativeTimes tms);
+    
+    int flock(int fd, int mode);
+    int unlink(CharSequence path);
+    @Variadic(fixedCount = 2)
+    int open(CharSequence path, int flags, @u_int32_t int perm);
+    int pipe(@Out int[] fds);
+    int truncate(CharSequence path, long length);
+    int ftruncate(int fd, long offset);
+    int rename(CharSequence oldName, CharSequence newName);
+    long getcwd(byte[] cwd, int len);
+    int gethostname(@Out ByteBuffer buffer, int len);
+    int fsync(int fd);
+    int fdatasync(int fd);
+
+    int socketpair(int domain, int type, int protocol, @Out int[] fds);
+    int sendmsg(int socket, @In MsgHdr message, int flags);
+    int recvmsg(int socket, @Direct MsgHdr message, int flags);
+
+    int setsockopt(int s, int level, int optname, @In ByteBuffer optval, int optlen);
+    int getsockopt(int s, int level, int optname, @Out ByteBuffer optval, @In @Out IntByReference optlen);
+
+    Variable<Long> environ();
+
+    int syscall(int number);
+    int syscall(int number, int arg1);
+    int syscall(int number, int arg1, int arg2);
+    int syscall(int number, int arg1, int arg2, int arg3);
+
+    int daemon(int nochdir, int noclose);
+
+    int getgroups(int size, int[] groups);
+
+    String nl_langinfo(int item);
+    String setlocale(int category, String locale);
+
+    String strerror(int errno);
+
+    int gettimeofday(Timeval tv, long alwaysNull);
+}
+
diff --git a/src/main/java/jnr/posix/LibCProvider.java b/src/main/java/jnr/posix/LibCProvider.java
new file mode 100644
index 0000000..a94df89
--- /dev/null
+++ b/src/main/java/jnr/posix/LibCProvider.java
@@ -0,0 +1,7 @@
+
+package jnr.posix;
+
+public interface LibCProvider {
+    public LibC getLibC();
+    public Crypt getCrypt();
+}
diff --git a/src/main/java/jnr/posix/Linux.java b/src/main/java/jnr/posix/Linux.java
new file mode 100644
index 0000000..b7e7cb2
--- /dev/null
+++ b/src/main/java/jnr/posix/Linux.java
@@ -0,0 +1,12 @@
+package jnr.posix;
+
+import jnr.constants.platform.PosixFadvise;
+
+/**
+ * Linux-specific POSIX-like functions.
+ */
+public interface Linux extends POSIX {
+    int ioprio_get(int which, int who);
+    int ioprio_set(int which, int who, int ioprio);
+    int posix_fadvise(int fd, long offset, long len, PosixFadvise advise);
+}
diff --git a/src/main/java/jnr/posix/LinuxCmsgHdr.java b/src/main/java/jnr/posix/LinuxCmsgHdr.java
new file mode 100644
index 0000000..9854e9d
--- /dev/null
+++ b/src/main/java/jnr/posix/LinuxCmsgHdr.java
@@ -0,0 +1,73 @@
+package jnr.posix;
+
+import jnr.ffi.Pointer;
+import jnr.ffi.Runtime;
+import jnr.ffi.StructLayout;
+
+/**
+ * @author Bob McWhirter
+ */
+class LinuxCmsgHdr extends BaseCmsgHdr {
+
+    public static class Layout extends StructLayout {
+
+        protected Layout(Runtime runtime) {
+            super(runtime);
+        }
+
+        // contrary to man page
+        public final size_t cmsg_len = new size_t();
+        public final Signed32 cmsg_level = new Signed32();
+        public final Signed32 cmsg_type = new Signed32();
+    }
+
+    public static final Layout layout = new Layout(Runtime.getSystemRuntime());
+
+    public LinuxCmsgHdr(NativePOSIX posix, Pointer memory) {
+        super(posix, memory);
+    }
+
+    public LinuxCmsgHdr(NativePOSIX posix, Pointer memory, int totalLen) {
+        super(posix, memory, totalLen);
+    }
+
+    public void setLevel(int level) {
+        layout.cmsg_level.set(this.memory, level);
+    }
+
+    public int getLevel() {
+        return layout.cmsg_level.get(this.memory);
+    }
+
+    public void setType(int type) {
+        layout.cmsg_type.set(this.memory, type);
+    }
+
+    public int getType() {
+        return layout.cmsg_type.get(this.memory);
+    }
+
+    public int getLen() {
+        return (int) layout.cmsg_len.get(this.memory);
+    }
+
+    void setLen(int len) {
+        layout.cmsg_len.set(this.memory, len);
+    }
+
+    public String toString(String indent) {
+        StringBuffer buf = new StringBuffer();
+
+        buf.append(indent).append("cmsg {\n");
+        buf.append(indent).append("  cmsg_len=").append(layout.cmsg_len.get(this.memory)).append("\n");
+        buf.append(indent).append("  cmsg_level=").append(layout.cmsg_level.get(this.memory)).append("\n");
+        buf.append(indent).append("  cmsg_type=").append(layout.cmsg_type.get(this.memory)).append("\n");
+        buf.append(indent).append("  cmsg_data=").append(getData()).append("\n");
+        buf.append(indent).append("}");
+        return buf.toString();
+    }
+
+    public String toString(){
+        return toString( "" );
+    }
+}
diff --git a/src/main/java/jnr/posix/LinuxFileStat32.java b/src/main/java/jnr/posix/LinuxFileStat32.java
new file mode 100644
index 0000000..333fee2
--- /dev/null
+++ b/src/main/java/jnr/posix/LinuxFileStat32.java
@@ -0,0 +1,140 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: EPL 2.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Eclipse Public
+ * License Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+/**
+ * $Id: $
+ */
+
+package jnr.posix;
+
+import jnr.ffi.*;
+
+public final class LinuxFileStat32 extends BaseFileStat implements NanosecondFileStat {
+    private static final class Layout extends StructLayout {
+
+        private Layout(jnr.ffi.Runtime runtime) {
+            super(runtime);
+        }
+
+        public final Signed64 st_dev = new Signed64();
+        public final Signed16 __pad1 = new Signed16();
+        public final Signed32 st_ino = new Signed32();
+        public final Signed32 st_mode = new Signed32();
+        public final Signed32 st_nlink = new Signed32();
+        public final Signed32 st_uid = new Signed32();
+        public final Signed32 st_gid = new Signed32();
+        public final Signed64 st_rdev = new Signed64();
+        public final Signed16 __pad2 = new Signed16();
+        public final Signed64 st_size = new Signed64();
+        public final Signed32 st_blksize = new Signed32();
+        public final Signed32 st_blocks = new Signed32();
+        public final Signed32 __unused4 = new Signed32();
+        public final Signed32 st_atim_sec = new Signed32();     // Time of last access (time_t)
+        public final Signed32 st_atim_nsec = new Signed32(); // Time of last access (nanoseconds)
+        public final Signed32 st_mtim_sec = new Signed32();     // Last data modification time (time_t)
+        public final Signed32 st_mtim_nsec = new Signed32(); // Last data modification time (nanoseconds)
+        public final Signed32 st_ctim_sec = new Signed32();     // Time of last status change (time_t)
+        public final Signed32 st_ctim_nsec = new Signed32(); // Time of last status change (nanoseconds)
+        public final Signed64 __unused5 = new Signed64();
+    }
+    private static final Layout layout = new Layout(jnr.ffi.Runtime.getSystemRuntime());
+
+    public LinuxFileStat32() {
+        this(null);
+    }
+
+    public LinuxFileStat32(BaseNativePOSIX posix) {
+        super(posix, layout);
+    }
+    
+    public long atime() {
+        return layout.st_atim_sec.get(memory);
+    }
+
+    @Override
+    public long aTimeNanoSecs() {
+        return layout.st_atim_nsec.get(memory);
+    }
+
+    public long blocks() {
+        return layout.st_blocks.get(memory);
+    }
+
+    public long blockSize() {
+        return layout.st_blksize.get(memory);
+    }
+
+    public long ctime() {
+        return layout.st_ctim_sec.get(memory);
+    }
+
+    @Override
+    public long cTimeNanoSecs() {
+        return layout.st_ctim_nsec.get(memory);
+    }
+
+    public long dev() {
+        return layout.st_dev.get(memory);
+    }
+
+    public int gid() {
+        return layout.st_gid.get(memory);
+    }
+
+    public long ino() {
+        return layout.st_ino.get(memory);
+    }
+
+    public int mode() {
+        return layout.st_mode.get(memory) & 0xffff;
+    }
+
+    public long mtime() {
+        return layout.st_mtim_sec.get(memory);
+    }
+
+    @Override
+    public long mTimeNanoSecs() {
+        return layout.st_mtim_nsec.get(memory);
+    }
+
+    public int nlink() {
+        return layout.st_nlink.get(memory);
+    }
+
+    public long rdev() {
+        return layout.st_rdev.get(memory);
+    }
+
+    public long st_size() {
+        return layout.st_size.get(memory);
+    }
+
+    public int uid() {
+        return layout.st_uid.get(memory);
+    }
+}
diff --git a/src/main/java/jnr/posix/LinuxFileStat64.java b/src/main/java/jnr/posix/LinuxFileStat64.java
new file mode 100644
index 0000000..1a51523
--- /dev/null
+++ b/src/main/java/jnr/posix/LinuxFileStat64.java
@@ -0,0 +1,102 @@
+package jnr.posix;
+
+import jnr.ffi.StructLayout;
+
+public final class LinuxFileStat64 extends BaseFileStat implements NanosecondFileStat {
+    public static final class Layout extends StructLayout {
+
+        public Layout(jnr.ffi.Runtime runtime) {
+            super(runtime);
+        }
+
+        public final dev_t st_dev = new dev_t();
+        public final ino_t st_ino = new ino_t();
+        public final nlink_t st_nlink = new nlink_t();
+        public final mode_t st_mode = new mode_t();
+        public final uid_t st_uid = new uid_t();
+        public final gid_t st_gid = new gid_t();
+        public final dev_t st_rdev = new dev_t();
+        public final off_t st_size = new off_t();
+        public final blksize_t st_blksize = new blksize_t();
+        public final blkcnt_t st_blocks = new blkcnt_t();
+        public final time_t st_atime = new time_t();     // Time of last access (time_t)
+        public final SignedLong st_atimensec = new SignedLong(); // Time of last access (nanoseconds)
+        public final time_t st_mtime = new time_t();     // Last data modification time (time_t)
+        public final SignedLong st_mtimensec = new SignedLong(); // Last data modification time (nanoseconds)
+        public final time_t st_ctime = new time_t();     // Time of last status change (time_t)
+        public final SignedLong st_ctimensec = new SignedLong(); // Time of last status change (nanoseconds)
+        public final Signed64 __unused4 = new Signed64();
+        public final Signed64 __unused5 = new Signed64();
+        public final Signed64 __unused6 = new Signed64();
+    }
+
+    private static final Layout layout = new Layout(jnr.ffi.Runtime.getSystemRuntime());
+
+    public LinuxFileStat64(LinuxPOSIX posix) {
+        super(posix, layout);
+    }
+
+    public long atime() {
+        return layout.st_atime.get(memory);
+    }
+
+    public long aTimeNanoSecs() {
+        return layout.st_atimensec.get(memory);
+    }
+
+    public long blockSize() {
+        return layout.st_blksize.get(memory);
+    }
+
+    public long blocks() {
+        return layout.st_blocks.get(memory);
+    }
+
+    public long ctime() {
+        return layout.st_ctime.get(memory);
+    }
+
+    public long cTimeNanoSecs() {
+        return layout.st_ctimensec.get(memory);
+    }
+
+    public long dev() {
+        return layout.st_dev.get(memory);
+    }
+
+    public int gid() {
+        return (int) layout.st_gid.get(memory);
+    }
+
+    public long ino() {
+        return layout.st_ino.get(memory);
+    }
+
+    public int mode() {
+        return (int) layout.st_mode.get(memory);
+    }
+
+    public long mtime() {
+        return layout.st_mtime.get(memory);
+    }
+
+    public long mTimeNanoSecs() {
+        return layout.st_mtimensec.get(memory);
+    }
+
+    public int nlink() {
+        return (int) layout.st_nlink.get(memory);
+    }
+
+    public long rdev() {
+        return layout.st_rdev.get(memory);
+    }
+
+    public long st_size() {
+        return layout. st_size.get(memory);
+    }
+
+    public int uid() {
+        return (int) layout.st_uid.get(memory);
+    }
+}
diff --git a/src/main/java/jnr/posix/LinuxFileStatAARCH64.java b/src/main/java/jnr/posix/LinuxFileStatAARCH64.java
new file mode 100644
index 0000000..d5df659
--- /dev/null
+++ b/src/main/java/jnr/posix/LinuxFileStatAARCH64.java
@@ -0,0 +1,104 @@
+package jnr.posix;
+
+import jnr.ffi.StructLayout;
+import jnr.posix.util.Platform;
+
+public final class LinuxFileStatAARCH64 extends BaseFileStat implements NanosecondFileStat {
+    public static final class Layout extends StructLayout {
+
+        public Layout(jnr.ffi.Runtime runtime) {
+            super(runtime);
+        }
+
+        public final dev_t st_dev = new dev_t();
+        public final ino_t st_ino = new ino_t();
+        public final mode_t st_mode = new mode_t();
+        public final nlink_t st_nlink = new nlink_t();
+        public final uid_t st_uid = new uid_t();
+        public final gid_t st_gid = new gid_t();
+        public final dev_t st_rdev = new dev_t();
+        public final dev_t __pad1 = new dev_t();
+        public final off_t st_size = new off_t();
+        public final blksize_t st_blksize = new blksize_t();
+        public final Signed32 __pad2 = new Signed32();
+        public final blkcnt_t st_blocks = new blkcnt_t();
+        public final time_t st_atime = new time_t();             // Time of last access
+        public final SignedLong st_atimensec = new SignedLong(); // Time of last access (nanoseconds)
+        public final time_t st_mtime = new time_t();             // Last data modification time
+        public final SignedLong st_mtimensec = new SignedLong(); // Last data modification time (nanoseconds)
+        public final time_t st_ctime = new time_t();             // Time of last status change
+        public final SignedLong st_ctimensec = new SignedLong(); // Time of last status change (nanoseconds)
+        public final Signed32 __unused4 = new Signed32();
+        public final Signed32 __unused5 = new Signed32();
+    }
+
+    private static final Layout layout = new Layout(jnr.ffi.Runtime.getSystemRuntime());
+
+    public LinuxFileStatAARCH64(LinuxPOSIX posix) {
+        super(posix, layout);
+    }
+
+    public long atime() {
+        return layout.st_atime.get(memory);
+    }
+
+    public long aTimeNanoSecs() {
+        return layout.st_atimensec.get(memory);
+    }
+
+    public long blockSize() {
+        return layout.st_blksize.get(memory);
+    }
+
+    public long blocks() {
+        return layout.st_blocks.get(memory);
+    }
+
+    public long ctime() {
+        return layout.st_ctime.get(memory);
+    }
+
+    public long cTimeNanoSecs() {
+        return layout.st_ctimensec.get(memory);
+    }
+
+    public long dev() {
+        return layout.st_dev.get(memory);
+    }
+
+    public int gid() {
+        return (int) layout.st_gid.get(memory);
+    }
+
+    public long ino() {
+        return layout.st_ino.get(memory);
+    }
+
+    public int mode() {
+        return (int) layout.st_mode.get(memory);
+    }
+
+    public long mtime() {
+        return layout.st_mtime.get(memory);
+    }
+
+    public long mTimeNanoSecs() {
+        return layout.st_mtimensec.get(memory);
+    }
+
+    public int nlink() {
+        return (int) layout.st_nlink.get(memory);
+    }
+
+    public long rdev() {
+        return layout.st_rdev.get(memory);
+    }
+
+    public long st_size() {
+        return layout. st_size.get(memory);
+    }
+
+    public int uid() {
+        return (int) layout.st_uid.get(memory);
+    }
+}
diff --git a/src/main/java/jnr/posix/LinuxFileStatLOONGARCH64.java b/src/main/java/jnr/posix/LinuxFileStatLOONGARCH64.java
new file mode 100644
index 0000000..54b1871
--- /dev/null
+++ b/src/main/java/jnr/posix/LinuxFileStatLOONGARCH64.java
@@ -0,0 +1,104 @@
+package jnr.posix;
+
+import jnr.ffi.StructLayout;
+import jnr.posix.util.Platform;
+
+public final class LinuxFileStatLOONGARCH64 extends BaseFileStat implements NanosecondFileStat {
+    public static final class Layout extends StructLayout {
+
+        public Layout(jnr.ffi.Runtime runtime) {
+            super(runtime);
+        }
+
+        public final dev_t st_dev = new dev_t();
+        public final ino_t st_ino = new ino_t();
+        public final mode_t st_mode = new mode_t();
+        public final nlink_t st_nlink = new nlink_t();
+        public final uid_t st_uid = new uid_t();
+        public final gid_t st_gid = new gid_t();
+        public final dev_t st_rdev = new dev_t();
+        public final dev_t __pad1 = new dev_t();
+        public final off_t st_size = new off_t();
+        public final blksize_t st_blksize = new blksize_t();
+        public final Signed32 __pad2 = new Signed32();
+        public final blkcnt_t st_blocks = new blkcnt_t();
+        public final time_t st_atime = new time_t();             // Time of last access
+        public final SignedLong st_atimensec = new SignedLong(); // Time of last access (nanoseconds)
+        public final time_t st_mtime = new time_t();             // Last data modification time
+        public final SignedLong st_mtimensec = new SignedLong(); // Last data modification time (nanoseconds)
+        public final time_t st_ctime = new time_t();             // Time of last status change
+        public final SignedLong st_ctimensec = new SignedLong(); // Time of last status change (nanoseconds)
+        public final Signed32 __unused4 = new Signed32();
+        public final Signed32 __unused5 = new Signed32();
+    }
+
+    private static final Layout layout = new Layout(jnr.ffi.Runtime.getSystemRuntime());
+
+    public LinuxFileStatLOONGARCH64(LinuxPOSIX posix) {
+        super(posix, layout);
+    }
+
+    public long atime() {
+        return layout.st_atime.get(memory);
+    }
+
+    public long aTimeNanoSecs() {
+        return layout.st_atimensec.get(memory);
+    }
+
+    public long blockSize() {
+        return layout.st_blksize.get(memory);
+    }
+
+    public long blocks() {
+        return layout.st_blocks.get(memory);
+    }
+
+    public long ctime() {
+        return layout.st_ctime.get(memory);
+    }
+
+    public long cTimeNanoSecs() {
+        return layout.st_ctimensec.get(memory);
+    }
+
+    public long dev() {
+        return layout.st_dev.get(memory);
+    }
+
+    public int gid() {
+        return (int) layout.st_gid.get(memory);
+    }
+
+    public long ino() {
+        return layout.st_ino.get(memory);
+    }
+
+    public int mode() {
+        return (int) layout.st_mode.get(memory);
+    }
+
+    public long mtime() {
+        return layout.st_mtime.get(memory);
+    }
+
+    public long mTimeNanoSecs() {
+        return layout.st_mtimensec.get(memory);
+    }
+
+    public int nlink() {
+        return (int) layout.st_nlink.get(memory);
+    }
+
+    public long rdev() {
+        return layout.st_rdev.get(memory);
+    }
+
+    public long st_size() {
+        return layout. st_size.get(memory);
+    }
+
+    public int uid() {
+        return (int) layout.st_uid.get(memory);
+    }
+}
diff --git a/src/main/java/jnr/posix/LinuxFileStatMIPS64.java b/src/main/java/jnr/posix/LinuxFileStatMIPS64.java
new file mode 100644
index 0000000..ac16548
--- /dev/null
+++ b/src/main/java/jnr/posix/LinuxFileStatMIPS64.java
@@ -0,0 +1,110 @@
+package jnr.posix;
+
+import jnr.ffi.StructLayout;
+
+public final class LinuxFileStatMIPS64 extends BaseFileStat implements NanosecondFileStat {
+    public static final class Layout extends StructLayout {
+
+        public Layout(jnr.ffi.Runtime runtime) {
+            super(runtime);
+        }
+
+        public final Unsigned64 st_dev = new Unsigned64();
+        public final Unsigned32 __pad01 = new Unsigned32();
+        public final Unsigned32 __pad02 = new Unsigned32();
+        public final Unsigned32 __pad03 = new Unsigned32();
+
+        public final Unsigned64 st_ino = new Unsigned64();
+        public final Unsigned64 st_mode = new Unsigned64();
+        public final Unsigned32 st_nlink = new Unsigned32();
+        public final Unsigned32 st_uid = new Unsigned32();
+        public final Unsigned32 st_gid = new Unsigned32();
+        public final Unsigned64 st_rdev = new Unsigned64();
+	public final Unsigned32 __pad11 = new Unsigned32();
+        public final Unsigned32 __pad12 = new Unsigned32();
+        public final Unsigned32 __pad13 = new Unsigned32();
+
+        public final Signed64	st_size = new Signed64();
+
+        public final Unsigned64	st_atime = new Unsigned64();     // Time of last access (time_t)
+        public final Unsigned64	st_atimensec = new Unsigned64(); // Time of last access (nanoseconds)
+        public final Unsigned64	st_mtime = new Unsigned64();     // Last data modification time (time_t)
+        public final Unsigned64	st_mtimensec = new Unsigned64(); // Last data modification time (nanoseconds)
+        public final Unsigned64	st_ctime = new Unsigned64();     // Time of last status change (time_t)
+        public final Unsigned64	st_ctimensec = new Unsigned64(); // Time of last status change (nanoseconds)
+
+        public final Unsigned64	st_blksize = new Unsigned64();
+        public final Unsigned32	__pad20 = new Unsigned32();
+        public final Unsigned64	st_blocks = new Unsigned64();
+    }
+
+    private static final Layout layout = new Layout(jnr.ffi.Runtime.getSystemRuntime());
+
+    public LinuxFileStatMIPS64(LinuxPOSIX posix) {
+        super(posix, layout);
+    }
+
+    public long atime() {
+        return layout.st_atime.get(memory);
+    }
+
+    public long aTimeNanoSecs() {
+        return layout.st_atimensec.get(memory);
+    }
+
+    public long blockSize() {
+        return layout.st_blksize.get(memory);
+    }
+
+    public long blocks() {
+        return layout.st_blocks.get(memory);
+    }
+
+    public long ctime() {
+        return layout.st_ctime.get(memory);
+    }
+
+    public long cTimeNanoSecs() {
+        return layout.st_ctimensec.get(memory);
+    }
+
+    public long dev() {
+        return layout.st_dev.get(memory);
+    }
+
+    public int gid() {
+        return (int) layout.st_gid.get(memory);
+    }
+
+    public long ino() {
+        return layout.st_ino.get(memory);
+    }
+
+    public int mode() {
+        return (int) layout.st_mode.get(memory);
+    }
+
+    public long mtime() {
+        return layout.st_mtime.get(memory);
+    }
+
+    public long mTimeNanoSecs() {
+        return layout.st_mtimensec.get(memory);
+    }
+
+    public int nlink() {
+        return (int) layout.st_nlink.get(memory);
+    }
+
+    public long rdev() {
+        return layout.st_rdev.get(memory);
+    }
+
+    public long st_size() {
+        return layout. st_size.get(memory);
+    }
+
+    public int uid() {
+        return (int) layout.st_uid.get(memory);
+    }
+}
diff --git a/src/main/java/jnr/posix/LinuxFileStatRISCV64.java b/src/main/java/jnr/posix/LinuxFileStatRISCV64.java
new file mode 100644
index 0000000..94093ea
--- /dev/null
+++ b/src/main/java/jnr/posix/LinuxFileStatRISCV64.java
@@ -0,0 +1,104 @@
+package jnr.posix;
+
+import jnr.ffi.StructLayout;
+import jnr.posix.util.Platform;
+
+public final class LinuxFileStatRISCV64 extends BaseFileStat implements NanosecondFileStat {
+    public static final class Layout extends StructLayout {
+
+        public Layout(jnr.ffi.Runtime runtime) {
+            super(runtime);
+        }
+
+        public final dev_t st_dev = new dev_t();
+        public final ino_t st_ino = new ino_t();
+        public final mode_t st_mode = new mode_t();
+        public final nlink_t st_nlink = new nlink_t();
+        public final uid_t st_uid = new uid_t();
+        public final gid_t st_gid = new gid_t();
+        public final dev_t st_rdev = new dev_t();
+        public final dev_t __pad1 = new dev_t();
+        public final off_t st_size = new off_t();
+        public final blksize_t st_blksize = new blksize_t();
+        public final Signed32 __pad2 = new Signed32();
+        public final blkcnt_t st_blocks = new blkcnt_t();
+        public final time_t st_atime = new time_t();             // Time of last access
+        public final SignedLong st_atimensec = new SignedLong(); // Time of last access (nanoseconds)
+        public final time_t st_mtime = new time_t();             // Last data modification time
+        public final SignedLong st_mtimensec = new SignedLong(); // Last data modification time (nanoseconds)
+        public final time_t st_ctime = new time_t();             // Time of last status change
+        public final SignedLong st_ctimensec = new SignedLong(); // Time of last status change (nanoseconds)
+        public final Signed32 __unused4 = new Signed32();
+        public final Signed32 __unused5 = new Signed32();
+    }
+
+    private static final Layout layout = new Layout(jnr.ffi.Runtime.getSystemRuntime());
+
+    public LinuxFileStatRISCV64(LinuxPOSIX posix) {
+        super(posix, layout);
+    }
+
+    public long atime() {
+        return layout.st_atime.get(memory);
+    }
+
+    public long aTimeNanoSecs() {
+        return layout.st_atimensec.get(memory);
+    }
+
+    public long blockSize() {
+        return layout.st_blksize.get(memory);
+    }
+
+    public long blocks() {
+        return layout.st_blocks.get(memory);
+    }
+
+    public long ctime() {
+        return layout.st_ctime.get(memory);
+    }
+
+    public long cTimeNanoSecs() {
+        return layout.st_ctimensec.get(memory);
+    }
+
+    public long dev() {
+        return layout.st_dev.get(memory);
+    }
+
+    public int gid() {
+        return (int) layout.st_gid.get(memory);
+    }
+
+    public long ino() {
+        return layout.st_ino.get(memory);
+    }
+
+    public int mode() {
+        return (int) layout.st_mode.get(memory);
+    }
+
+    public long mtime() {
+        return layout.st_mtime.get(memory);
+    }
+
+    public long mTimeNanoSecs() {
+        return layout.st_mtimensec.get(memory);
+    }
+
+    public int nlink() {
+        return (int) layout.st_nlink.get(memory);
+    }
+
+    public long rdev() {
+        return layout.st_rdev.get(memory);
+    }
+
+    public long st_size() {
+        return layout. st_size.get(memory);
+    }
+
+    public int uid() {
+        return (int) layout.st_uid.get(memory);
+    }
+}
diff --git a/src/main/java/jnr/posix/LinuxFileStatSPARCV9.java b/src/main/java/jnr/posix/LinuxFileStatSPARCV9.java
new file mode 100644
index 0000000..bc620c7
--- /dev/null
+++ b/src/main/java/jnr/posix/LinuxFileStatSPARCV9.java
@@ -0,0 +1,103 @@
+package jnr.posix;
+
+import jnr.ffi.StructLayout;
+
+public class LinuxFileStatSPARCV9 extends BaseFileStat implements NanosecondFileStat {
+    public static final class Layout extends StructLayout {
+
+        public Layout(jnr.ffi.Runtime runtime) {
+            super(runtime);
+        }
+
+        public final dev_t st_dev = new dev_t();
+        public final Unsigned16 __pad1  = new Unsigned16();
+        public final ino_t st_ino = new ino_t();
+        public final mode_t st_mode = new mode_t();
+        public final nlink_t st_nlink = new nlink_t();
+        public final uid_t st_uid = new uid_t();
+        public final gid_t st_gid = new gid_t();
+        public final dev_t st_rdev = new dev_t();
+        public final Unsigned16 __pad2 = new Unsigned16();
+        public final off_t st_size = new off_t();
+        public final blksize_t st_blksize = new blksize_t();
+        public final blkcnt_t st_blocks = new blkcnt_t();
+        public final time_t st_atime = new time_t();     // Time of last access (time_t)
+        public final SignedLong st_atimensec = new SignedLong(); // Time of last access (nanoseconds)
+        public final time_t st_mtime = new time_t();     // Last data modification time (time_t)
+        public final SignedLong st_mtimensec = new SignedLong(); // Last data modification time (nanoseconds)
+        public final time_t st_ctime = new time_t();     // Time of last status change (time_t)
+        public final SignedLong st_ctimensec = new SignedLong(); // Time of last status change (nanoseconds)
+        public final Unsigned64 __unused4 = new Unsigned64();
+        public final Unsigned64 __unused5 = new Unsigned64();
+    }
+
+    public static final Layout layout = new Layout(jnr.ffi.Runtime.getSystemRuntime());
+
+    public LinuxFileStatSPARCV9(LinuxPOSIX posix) {
+        super(posix, layout);
+    }
+
+    public long atime() {
+        return layout.st_atime.get(memory);
+    }
+
+    public long aTimeNanoSecs() {
+        return layout.st_atimensec.get(memory);
+    }
+
+    public long blockSize() {
+        return layout.st_blksize.get(memory);
+    }
+
+    public long blocks() {
+        return layout.st_blocks.get(memory);
+    }
+
+    public long ctime() {
+        return layout.st_ctime.get(memory);
+    }
+
+    public long cTimeNanoSecs() {
+        return layout.st_ctimensec.get(memory);
+    }
+
+    public long dev() {
+        return layout.st_dev.get(memory);
+    }
+
+    public int gid() {
+        return (int) layout.st_gid.get(memory);
+    }
+
+    public long ino() {
+        return layout.st_ino.get(memory);
+    }
+
+    public int mode() {
+        return (int) layout.st_mode.get(memory);
+    }
+
+    public long mtime() {
+        return layout.st_mtime.get(memory);
+    }
+
+    public long mTimeNanoSecs() {
+        return layout.st_mtimensec.get(memory);
+    }
+
+    public int nlink() {
+        return (int) layout.st_nlink.get(memory);
+    }
+
+    public long rdev() {
+        return layout.st_rdev.get(memory);
+    }
+
+    public long st_size() {
+        return layout. st_size.get(memory);
+    }
+
+    public int uid() {
+        return (int) layout.st_uid.get(memory);
+    }
+}
diff --git a/src/main/java/jnr/posix/LinuxIoPrio.java b/src/main/java/jnr/posix/LinuxIoPrio.java
new file mode 100644
index 0000000..3b09993
--- /dev/null
+++ b/src/main/java/jnr/posix/LinuxIoPrio.java
@@ -0,0 +1,24 @@
+package jnr.posix;
+
+public abstract class LinuxIoPrio {
+    public static int IOPRIO_WHO_PROCESS = 1;
+    public static int IOPRIO_WHO_PGRP = 2;
+    public static int IOPRIO_WHO_USER = 3;
+
+    public static int IOPRIO_CLASS_NONE = 0;
+    public static int IOPRIO_CLASS_RT = 1;
+    public static int IOPRIO_CLASS_BE = 2;
+    public static int IOPRIO_CLASS_IDLE = 3;
+
+    public static int IOPRIO_PRIO_VALUE(int _class, int data) {
+        return (_class << 13) | data;
+    }
+
+    public static int IOPRIO_PRIO_CLASS(int mask) {
+        return mask >> 13;
+    }
+
+    public static int IOPRIO_PRIO_DATA(int mask) {
+        return mask & 0x0f;
+    }
+}
diff --git a/src/main/java/jnr/posix/LinuxLibC.java b/src/main/java/jnr/posix/LinuxLibC.java
new file mode 100644
index 0000000..3275843
--- /dev/null
+++ b/src/main/java/jnr/posix/LinuxLibC.java
@@ -0,0 +1,22 @@
+package jnr.posix;
+
+import jnr.ffi.annotations.In;
+import jnr.ffi.annotations.NulTerminate;
+import jnr.ffi.annotations.Out;
+import jnr.ffi.annotations.Transient;
+import jnr.ffi.types.off_t;
+import java.nio.ByteBuffer;
+
+public interface LinuxLibC extends UnixLibC {
+    public int __fxstat(int version, int fd, @Out @Transient FileStat stat);
+    public int __lxstat(int version, CharSequence path, @Out @Transient FileStat stat);
+    public int __lxstat(int version, @NulTerminate @In ByteBuffer path, @Out @Transient FileStat stat);
+    public int __xstat(int version, CharSequence path, @Out @Transient FileStat stat);
+    public int __xstat(int version, @NulTerminate @In ByteBuffer path, @Out @Transient FileStat stat);
+    public int __fxstat64(int version, int fd, @Out @Transient FileStat stat);
+    public int __lxstat64(int version, CharSequence path, @Out @Transient FileStat stat);
+    public int __lxstat64(int version, @NulTerminate @In ByteBuffer path, @Out @Transient FileStat stat);
+    public int __xstat64(int version, CharSequence path, @Out @Transient FileStat stat);
+    public int __xstat64(int version, @NulTerminate @In ByteBuffer path, @Out @Transient FileStat stat);
+    public int posix_fadvise(int fd, @off_t long offset, @off_t long len, int advice);
+}
diff --git a/src/main/java/jnr/posix/LinuxMsgHdr.java b/src/main/java/jnr/posix/LinuxMsgHdr.java
new file mode 100644
index 0000000..039479c
--- /dev/null
+++ b/src/main/java/jnr/posix/LinuxMsgHdr.java
@@ -0,0 +1,173 @@
+package jnr.posix;
+
+import java.util.ArrayList;
+import java.util.List;
+import jnr.ffi.Pointer;
+import jnr.ffi.Runtime;
+import jnr.ffi.StructLayout;
+
+/**
+ * @author Bob McWhirter
+ */
+class LinuxMsgHdr extends BaseMsgHdr {
+
+    public static class Layout extends StructLayout {
+        protected Layout(Runtime runtime) {
+            super(runtime);
+        }
+
+        public final Pointer msg_name = new Pointer();
+        public final socklen_t msg_namelen = new socklen_t();
+        public final Pointer msg_iov = new Pointer();
+        public final size_t msg_iovlen = new size_t();
+        public final Pointer msg_control = new Pointer();
+        public final size_t msg_controllen = new size_t();
+        public final Signed32 msg_flags = new Signed32();
+    }
+
+    private static final Layout layout = new Layout(Runtime.getSystemRuntime());
+
+    protected LinuxMsgHdr(NativePOSIX posix) {
+        super(posix, layout);
+        setName(null);
+    }
+
+    CmsgHdr allocateCmsgHdrInternal(NativePOSIX posix, Pointer pointer, int len) {
+        if (len > 0) {
+            return new LinuxCmsgHdr(posix, pointer, len);
+        } else {
+            return new LinuxCmsgHdr(posix, pointer);
+        }
+    }
+
+    @Override
+    void setControlPointer(Pointer control) {
+        layout.msg_control.set(this.memory, control);
+    }
+
+    @Override
+    void setControlLen(int len) {
+        layout.msg_controllen.set(this.memory, len);
+    }
+
+    public String toString() {
+        StringBuffer buf = new StringBuffer();
+        buf.append("msghdr {\n");
+        buf.append("  msg_name=").append(getName()).append(",\n");
+        buf.append("  msg_namelen=").append(getNameLen()).append(",\n");
+
+        buf.append("  msg_iov=[\n");
+        Pointer iovp = layout.msg_iov.get(this.memory);
+
+        int numIov = getIovLen();
+        for (int i = 0; i < numIov; ++i) {
+            Pointer eachp = iovp.slice(i * BaseIovec.layout.size());
+            buf.append(new BaseIovec(posix, eachp).toString("    "));
+            if (i < (numIov - 1)) {
+                buf.append(",\n");
+            } else {
+                buf.append("\n");
+            }
+        }
+        buf.append("  ],\n");
+
+        buf.append("  msg_control=[\n");
+
+        CmsgHdr[] controls = getControls();
+        for (int i = 0; i < controls.length; ++i) {
+            buf.append(((LinuxCmsgHdr) controls[i]).toString("    "));
+            if (i < controls.length - 1) {
+                buf.append(",\n");
+            } else {
+                buf.append("\n");
+            }
+        }
+        buf.append("  ],\n");
+        buf.append("  msg_controllen=").append(layout.msg_controllen.get(this.memory)).append("\n");
+
+        buf.append("  msg_iovlen=").append(getIovLen()).append(",\n");
+        buf.append("  msg_flags=").append(getFlags()).append(",\n");
+        buf.append("}");
+        return buf.toString();
+    }
+
+    @Override
+    void setNamePointer(Pointer name) {
+        layout.msg_name.set( this.memory, name );
+    }
+
+    @Override
+    Pointer getNamePointer() {
+        return layout.msg_name.get( this.memory );
+    }
+
+
+    @Override
+    void setNameLen(int len) {
+        layout.msg_namelen.set(this.memory, len);
+    }
+
+    @Override
+    int getNameLen() {
+        return (int) layout.msg_namelen.get(this.memory);
+    }
+
+    @Override
+    void setIovPointer(Pointer iov) {
+        layout.msg_iov.set(this.memory, iov);
+    }
+
+    @Override
+    Pointer getIovPointer() {
+        return layout.msg_iov.get( this.memory );
+    }
+
+    @Override
+    void setIovLen(int len) {
+        layout.msg_iovlen.set(this.memory, len);
+    }
+
+    @Override
+    int getIovLen() {
+        return (int) layout.msg_iovlen.get(this.memory);
+    }
+
+    @Override
+    Pointer getControlPointer() {
+        return layout.msg_control.get(this.memory);
+    }
+
+    public int getControlLen() {
+        return (int) layout.msg_controllen.get(this.memory);
+    }
+
+    public void setFlags(int flags) {
+        layout.msg_flags.set(this.memory, flags);
+    }
+
+    public int getFlags() {
+        return layout.msg_flags.get(this.memory);
+    }
+
+    @Override
+    public CmsgHdr[] getControls() {
+        int len = getControlLen();
+        if (len == 0) {
+            return new CmsgHdr[0];
+        }
+
+        List<CmsgHdr> control = new ArrayList<CmsgHdr>();
+
+        int offset = 0;
+
+        Pointer controlPtr = getControlPointer();
+
+        while (offset < len) {
+            CmsgHdr each = allocateCmsgHdrInternal(posix, controlPtr.slice(offset), -1);
+            offset += LinuxSocketMacros.INSTANCE.CMSG_ALIGN( each.getLen() );
+            control.add(each);
+        }
+
+        return control.toArray(new CmsgHdr[control.size()]);
+    }
+}
diff --git a/src/main/java/jnr/posix/LinuxPOSIX.java b/src/main/java/jnr/posix/LinuxPOSIX.java
new file mode 100644
index 0000000..4a65400
--- /dev/null
+++ b/src/main/java/jnr/posix/LinuxPOSIX.java
@@ -0,0 +1,357 @@
+package jnr.posix;
+
+import jnr.constants.platform.Errno;
+import jnr.constants.platform.PosixFadvise;
+import jnr.constants.platform.Sysconf;
+import jnr.ffi.Memory;
+import jnr.ffi.Pointer;
+import jnr.ffi.mapper.FromNativeContext;
+import jnr.posix.util.Platform;
+
+import java.io.FileDescriptor;
+import java.nio.ByteBuffer;
+import jnr.constants.platform.Confstr;
+import jnr.constants.platform.Pathconf;
+
+final class LinuxPOSIX extends BaseNativePOSIX implements Linux {
+    private final boolean use_stat64;
+    private final int statVersion;
+
+    LinuxPOSIX(LibCProvider libcProvider, POSIXHandler handler) {
+        super(libcProvider, handler);
+
+        statVersion = getStatVersion();
+        use_stat64 = statVersion >= 0;
+    }
+
+    private int getStatVersion() {
+        if (Platform.IS_32_BIT || "sparcv9".equals(Platform.ARCH) || Platform.ARCH.contains("mips64")) {
+            return 3;
+        } else {
+            FileStat stat = allocateStat();
+            try {
+                if (((LinuxLibC) libc()).__xstat64(0, "/dev/null", stat) < 0) {
+                    return 1;
+                }
+                return 0;
+            } catch (UnsatisfiedLinkError ex) {
+                return -1;
+            }
+        }
+    }
+
+    @Override
+    public FileStat allocateStat() {
+        if (Platform.IS_32_BIT) {
+            return new LinuxFileStat32(this);
+        } else {
+            if ("aarch64".equals(Platform.ARCH)) {
+                return new LinuxFileStatAARCH64(this);
+            } else if ("riscv64".equals(Platform.ARCH)) {
+                return new LinuxFileStatRISCV64(this);
+            } else if ("sparcv9".equals(Platform.ARCH)) {
+		return new LinuxFileStatSPARCV9(this);
+	    } else if ("loongarch64".equals(Platform.ARCH)) {
+		return new LinuxFileStatLOONGARCH64(this);
+	    } else {
+		if (Platform.ARCH.contains("mips64")) {
+		    return new LinuxFileStatMIPS64(this);
+		}
+                return new LinuxFileStat64(this);
+	    }
+	}
+    }
+
+    public MsgHdr allocateMsgHdr() {
+        return new LinuxMsgHdr(this);
+    }
+
+    @Override
+    public Pointer allocatePosixSpawnFileActions() {
+        return Memory.allocateDirect(getRuntime(), 80);
+    }
+
+    @Override
+    public Pointer allocatePosixSpawnattr() {
+        return Memory.allocateDirect(getRuntime(), 336);
+    }
+
+    public SocketMacros socketMacros() {
+        return LinuxSocketMacros.INSTANCE;
+    }
+
+    private int old_fstat(int fd, FileStat stat) {
+        try {
+            return super.fstat(fd, stat);
+        } catch (UnsatisfiedLinkError ex2) {
+            handler.unimplementedError("fstat");
+            return -1;
+        }
+    }
+
+    @Override
+    public int fstat(int fd, FileStat stat) {
+        if (use_stat64) {
+            return ((LinuxLibC) libc()).__fxstat64(statVersion, fd, stat);
+        } else {
+            return old_fstat(fd, stat);
+        }
+    }
+
+    @Override
+    public FileStat fstat(int fd) {
+        FileStat stat = allocateStat();
+        int ret = fstat(fd, stat);
+        if (ret < 0) handler.error(Errno.valueOf(errno()), "fstat", Integer.toString(fd));
+        return stat;
+    }
+
+    @Override
+    public int fstat(FileDescriptor fileDescriptor, FileStat stat) {
+        return fstat(helper.getfd(fileDescriptor), stat);
+    }
+
+    @Override
+    public FileStat fstat(FileDescriptor fileDescriptor) {
+        FileStat stat = allocateStat();
+        int fd = helper.getfd(fileDescriptor);
+        int ret = fstat(fd, stat);
+        if (ret < 0) handler.error(Errno.valueOf(errno()), "fstat", Integer.toString(fd));
+        return stat;
+    }
+
+    private final int old_lstat(String path, FileStat stat) {
+        try {
+            return super.lstat(path, stat);
+        } catch (UnsatisfiedLinkError ex) {
+            handler.unimplementedError("lstat");
+            return -1;
+        }
+    }
+
+    @Override
+    public int lstat(String path, FileStat stat) {
+        if (use_stat64) {
+            return ((LinuxLibC) libc()).__lxstat64(statVersion, path, stat);
+        } else {
+            return old_lstat(path, stat);
+        }
+    }
+
+    @Override
+    public FileStat lstat(String path) {
+        FileStat stat = allocateStat();
+        int ret = lstat(path, stat);
+        if (ret < 0) handler.error(Errno.valueOf(errno()), "lstat", path);
+        return stat;
+    }
+
+    private final int old_stat(String path, FileStat stat) {
+        try {
+            return super.stat(path, stat);
+        } catch (UnsatisfiedLinkError ex) {
+            handler.unimplementedError("stat");
+            return -1;
+        }
+    }
+
+    @Override
+    public int stat(String path, FileStat stat) {
+
+        if (use_stat64) {
+            return ((LinuxLibC) libc()).__xstat64(statVersion, path, stat);
+        } else {
+            return old_stat(path, stat);
+        }
+    }
+
+    @Override
+    public FileStat stat(String path) {
+        FileStat stat = allocateStat();
+        int ret = stat(path, stat);
+        if (ret < 0) handler.error(Errno.valueOf(errno()), "stat", path);
+        return stat;
+    }
+
+    public long sysconf(Sysconf name) {
+        return libc().sysconf(name);
+    }
+
+    public int confstr(Confstr name, ByteBuffer buf, int len) {
+        return libc().confstr(name, buf, len);
+    }
+
+    public int fpathconf(int fd, Pathconf name) {
+        return libc().fpathconf(fd, name);
+    }
+
+    public Times times() {
+        return NativeTimes.times(this);
+    }
+
+    public static final PointerConverter PASSWD = new PointerConverter() {
+        public Object fromNative(Object arg, FromNativeContext ctx) {
+            return arg != null ? new LinuxPasswd((Pointer) arg) : null;
+        }
+    };
+
+    static final public class Syscall {
+        static final ABI _ABI_X86_32 = new ABI_X86_32();
+        static final ABI _ABI_X86_64 = new ABI_X86_64();
+        static final ABI _ABI_AARCH64 = new ABI_AARCH64();
+        static final ABI _ABI_SPARCV9 = new ABI_SPARCV9();
+        static final ABI _ABI_PPC64 = new ABI_PPC64();
+        static final ABI _ABI_MIPS64 = new ABI_MIPS64();
+        static final ABI _ABI_LOONGARCH64 = new ABI_LOONGARCH64();
+	static final ABI _ABI_RISCV64 = new ABI_RISCV64();
+
+        public static ABI abi() {
+            if ("x86_64".equals(Platform.ARCH)) {
+                if (Platform.IS_64_BIT) {
+                    return _ABI_X86_64;
+                }
+            } else if ("i386".equals(Platform.ARCH)) {
+                return _ABI_X86_32;
+            } else if ("aarch64".equals(Platform.ARCH)) {
+                return _ABI_AARCH64;
+            } else if ("sparcv9".equals(Platform.ARCH)) {
+                return _ABI_SPARCV9;
+            } else if (Platform.ARCH.contains("ppc64")) {
+                return _ABI_PPC64;
+            } else if (Platform.ARCH.contains("mips64")) {
+	    	return _ABI_MIPS64;
+	    } else if (Platform.ARCH.contains("loongarch64")) {
+		return _ABI_LOONGARCH64;
+	    } else if (Platform.ARCH.contains("riscv64")) {
+                return _ABI_RISCV64;
+	    }
+            return null;
+        }
+
+        interface ABI {
+            public int __NR_ioprio_set();
+            public int __NR_ioprio_get();
+        }
+
+        /** @see /usr/include/asm/unistd_32.h */
+        final static class ABI_X86_32 implements ABI {
+            @Override
+            public int __NR_ioprio_set() {
+                return 289;
+            }
+            @Override
+            public int __NR_ioprio_get() {
+                return 290;
+            }
+        }
+
+        /** @see /usr/include/asm/unistd_64.h */
+        final static class ABI_X86_64 implements ABI {
+            @Override
+            public int __NR_ioprio_set() {
+                return 251;
+            }
+            @Override
+            public int __NR_ioprio_get() {
+                return 252;
+            }
+        }
+
+        /** @see /usr/include/asm-generic/unistd.h */
+        final static class ABI_AARCH64 implements ABI {
+            @Override
+            public int __NR_ioprio_set() {
+                return 30;
+            }
+            @Override
+            public int __NR_ioprio_get() {
+                return 31 ;
+            }
+        }
+
+        /** @see /usr/include/asm/unistd.h */
+        final static class ABI_SPARCV9 implements ABI {
+            @Override
+            public int __NR_ioprio_set() {
+                return 196;
+            }
+            @Override
+            public int __NR_ioprio_get() {
+                return 218;
+            }
+        }
+
+        /** @see /usr/include/asm-generic/unistd.h */
+        final static class ABI_PPC64 implements ABI {
+            @Override
+            public int __NR_ioprio_set() {
+                return 273;
+            }
+            @Override
+            public int __NR_ioprio_get() {
+                return 274 ;
+            }
+        }
+
+        /** @see /usr/include/asm/unistd.h */
+        final static class ABI_MIPS64 implements ABI {
+            @Override
+            public int __NR_ioprio_set() {
+                return 5273;
+            }
+            @Override
+            public int __NR_ioprio_get() {
+                return 5274;
+            }
+        }
+
+        /** @see /usr/include/asm-generic/unistd.h */
+        final static class ABI_LOONGARCH64 implements ABI {
+            @Override
+            public int __NR_ioprio_set() {
+                return 30;
+            }
+            @Override
+            public int __NR_ioprio_get() {
+                return 31;
+            }
+        }
+	    
+	/** @see /usr/include/asm-generic/unistd.h */
+        final static class ABI_RISCV64 implements ABI {
+            @Override
+            public int __NR_ioprio_set() {
+                return 30;
+            }
+            @Override
+            public int __NR_ioprio_get() {
+                return 31 ;
+            }
+        }
+    }
+
+
+    public int ioprio_get(int which, int who) {
+        Syscall.ABI abi = Syscall.abi();
+        if (abi == null) {
+            handler.unimplementedError("ioprio_get");
+            return -1;
+        }
+
+        return libc().syscall(abi.__NR_ioprio_get(), which, who);
+    }
+
+    public int ioprio_set(int which, int who, int ioprio) {
+        Syscall.ABI abi = Syscall.abi();
+        if (abi == null) {
+            handler.unimplementedError("ioprio_set");
+            return -1;
+        }
+
+        return libc().syscall(abi.__NR_ioprio_set(), which, who, ioprio);
+    }
+
+    public int posix_fadvise(int fd, long offset, long len, PosixFadvise advise) {
+        return ((LinuxLibC) libc()).posix_fadvise(fd, offset, len, advise.intValue());
+    }
+}
diff --git a/src/main/java/jnr/posix/LinuxPasswd.java b/src/main/java/jnr/posix/LinuxPasswd.java
new file mode 100644
index 0000000..56669ba
--- /dev/null
+++ b/src/main/java/jnr/posix/LinuxPasswd.java
@@ -0,0 +1,57 @@
+
+package jnr.posix;
+
+import jnr.ffi.StructLayout;
+
+public final class LinuxPasswd extends NativePasswd implements Passwd {
+    private static final class Layout extends StructLayout {
+
+        private Layout(jnr.ffi.Runtime runtime) {
+            super(runtime);
+        }
+
+        public final UTF8StringRef pw_name = new UTF8StringRef();   // user name
+        public final UTF8StringRef pw_passwd = new UTF8StringRef(); // password (encrypted)
+        public final Signed32 pw_uid = new Signed32();       // user id
+        public final Signed32 pw_gid = new Signed32();       // user id
+        public final UTF8StringRef pw_gecos = new UTF8StringRef();  // login info
+        public final UTF8StringRef pw_dir = new UTF8StringRef();    // home directory
+        public final UTF8StringRef pw_shell = new UTF8StringRef();  // default shell
+    }
+    private static final Layout layout = new Layout(jnr.ffi.Runtime.getSystemRuntime());
+
+    LinuxPasswd(jnr.ffi.Pointer memory) {
+        super(memory);
+    }
+    
+    public java.lang.String getAccessClass() {
+        return "";
+    }
+    public java.lang.String getGECOS() {
+        return layout.pw_gecos.get(memory);
+    }
+    public long getGID() {
+        return layout.pw_gid.get(memory);
+    }
+    public java.lang.String getHome() {
+        return layout.pw_dir.get(memory);
+    }
+    public java.lang.String getLoginName() {
+        return layout.pw_name.get(memory);
+    }
+    public java.lang.String getPassword() {
+        return layout.pw_passwd.get(memory);
+    }
+    public java.lang.String getShell() {
+        return layout.pw_shell.get(memory);
+    }
+    public long getUID() {
+        return layout.pw_uid.get(memory);
+    }
+    public int getPasswdChangeTime() {
+        return 0;
+    }
+    public int getExpire() {
+        return Integer.MAX_VALUE;
+    }
+}
diff --git a/src/main/java/jnr/posix/LinuxSocketMacros.java b/src/main/java/jnr/posix/LinuxSocketMacros.java
new file mode 100644
index 0000000..d124f1c
--- /dev/null
+++ b/src/main/java/jnr/posix/LinuxSocketMacros.java
@@ -0,0 +1,29 @@
+package jnr.posix;
+
+import jnr.ffi.*;
+import jnr.ffi.Runtime;
+
+/**
+ * @author Bob McWhirter
+ */
+public class LinuxSocketMacros implements SocketMacros {
+
+    public static final LinuxSocketMacros INSTANCE = new LinuxSocketMacros();
+
+    public int CMSG_ALIGN(int len) {
+        int sizeof_size_t = Runtime.getSystemRuntime().findType(TypeAlias.size_t).size();
+        return (len + sizeof_size_t - 1) & ~(sizeof_size_t - 1);
+    }
+
+    public int CMSG_SPACE(int l) {
+        return CMSG_ALIGN(l) + CMSG_ALIGN(LinuxCmsgHdr.layout.size());
+    }
+
+    public int CMSG_LEN(int l) {
+        return CMSG_ALIGN( LinuxCmsgHdr.layout.size() ) + l;
+    }
+
+    public Pointer CMSG_DATA(Pointer cmsg) {
+        return cmsg.slice(CMSG_ALIGN(LinuxCmsgHdr.layout.size()));
+    }
+}
diff --git a/src/main/java/jnr/posix/MacOSCmsgHdr.java b/src/main/java/jnr/posix/MacOSCmsgHdr.java
new file mode 100644
index 0000000..d75c934
--- /dev/null
+++ b/src/main/java/jnr/posix/MacOSCmsgHdr.java
@@ -0,0 +1,69 @@
+package jnr.posix;
+
+import jnr.ffi.Pointer;
+import jnr.ffi.Runtime;
+import jnr.ffi.StructLayout;
+
+/**
+ * @author Bob McWhirter
+ */
+class MacOSCmsgHdr extends BaseCmsgHdr {
+
+    public static class Layout extends StructLayout {
+
+        protected Layout(Runtime runtime) {
+            super(runtime);
+        }
+
+        public final Unsigned32 cmsg_len = new Unsigned32();
+        public final Signed32 cmsg_level = new Signed32();
+        public final Signed32 cmsg_type = new Signed32();
+    }
+
+    public static final Layout layout = new Layout(Runtime.getSystemRuntime());
+
+    public MacOSCmsgHdr(NativePOSIX posix, Pointer memory) {
+        super(posix, memory);
+    }
+
+    public MacOSCmsgHdr(NativePOSIX posix, Pointer memory, int totalLen) {
+        super(posix, memory, totalLen );
+    }
+
+    public void setLevel(int level) {
+        layout.cmsg_level.set(this.memory, level);
+    }
+
+    public int getLevel() {
+        return layout.cmsg_level.get(this.memory);
+    }
+
+    public void setType(int type) {
+        layout.cmsg_type.set(this.memory, type);
+    }
+
+    public int getType() {
+        return layout.cmsg_type.get(this.memory);
+    }
+
+    public int getLen() {
+        return (int) layout.cmsg_len.get(this.memory);
+    }
+
+    void setLen(int len) {
+        layout.cmsg_len.set(this.memory, len);
+    }
+
+    public String toString(String indent) {
+        StringBuffer buf = new StringBuffer();
+
+        buf.append(indent).append("cmsg {\n");
+        buf.append(indent).append("  cmsg_len=").append(layout.cmsg_len.get(this.memory)).append("\n");
+        buf.append(indent).append("  cmsg_level=").append(layout.cmsg_level.get(this.memory)).append("\n");
+        buf.append(indent).append("  cmsg_type=").append(layout.cmsg_type.get(this.memory)).append("\n");
+        buf.append(indent).append("  cmsg_data=").append(getData()).append("\n");
+        buf.append(indent).append("}");
+        return buf.toString();
+    }
+
+}
diff --git a/src/main/java/jnr/posix/MacOSFileStat.java b/src/main/java/jnr/posix/MacOSFileStat.java
new file mode 100644
index 0000000..e9f42f2
--- /dev/null
+++ b/src/main/java/jnr/posix/MacOSFileStat.java
@@ -0,0 +1,139 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: EPL 2.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Eclipse Public
+ * License Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * 
+ *  
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+/**
+ * $Id: $
+ */
+
+package jnr.posix;
+
+import jnr.ffi.StructLayout;
+
+public final class MacOSFileStat extends BaseFileStat implements NanosecondFileStat {
+    public static class Layout extends StructLayout {
+
+        public Layout(jnr.ffi.Runtime runtime) {
+            super(runtime);
+        }
+
+        public final class time_t extends SignedLong {
+        }
+        public final Signed32 st_dev = new Signed32();
+        public final Signed32 st_ino = new Signed32();
+        public final Signed16 st_mode = new Signed16();
+        public final Signed16 st_nlink = new Signed16();
+        public final Signed32 st_uid = new Signed32();
+        public final Signed32 st_gid = new Signed32();
+        public final Signed32 st_rdev = new Signed32();
+        public final time_t st_atime = new time_t();
+        public final SignedLong st_atimensec = new SignedLong();
+        public final time_t st_mtime = new time_t();
+        public final SignedLong st_mtimensec = new SignedLong();
+        public final time_t st_ctime = new time_t();
+        public final SignedLong st_ctimensec = new SignedLong();
+        public final Signed64 st_size = new Signed64();
+        public final Signed64 st_blocks = new Signed64();
+        public final Signed32 st_blksize = new Signed32();
+        public final Signed32 st_flags = new Signed32();
+        public final Signed32 st_gen = new Signed32();
+        public final Signed32 st_lspare = new Signed32();
+        public final Signed64 st_qspare0 = new Signed64();
+        public final Signed64 st_qspare1 = new Signed64();
+    }
+    private static final Layout layout = new Layout(jnr.ffi.Runtime.getSystemRuntime());
+
+    public MacOSFileStat(MacOSPOSIX posix) {
+        super(posix, layout);
+    }
+
+    public long atime() {
+        return layout.st_atime.get(memory);
+    }
+
+    public long blocks() {
+        return layout.st_blocks.get(memory);
+    }
+
+    public long blockSize() {
+        return layout.st_blksize.get(memory);
+    }
+
+    public long ctime() {
+        return layout.st_ctime.get(memory);
+    }
+
+    public long dev() {
+        return layout.st_dev.get(memory);
+    }
+
+    public int gid() {
+        return layout.st_gid.get(memory);
+    }
+
+    public long ino() {
+        return layout.st_ino.get(memory);
+    }
+
+    public int mode() {
+        return layout.st_mode.get(memory) & 0xffff;
+    }
+
+    public long mtime() {
+        return layout.st_mtime.get(memory);
+    }
+
+    public int nlink() {
+        return layout.st_nlink.get(memory);
+    }
+
+    public long rdev() {
+        return layout.st_rdev.get(memory);
+    }
+
+    public long st_size() {
+        return layout.st_size.get(memory);
+    }
+
+    public int uid() {
+        return layout.st_uid.get(memory);
+    }
+
+    @Override
+    public long aTimeNanoSecs() {
+        return layout.st_atimensec.get(memory);
+    }
+
+    @Override
+    public long cTimeNanoSecs() {
+        return layout.st_ctimensec.get(memory);
+    }
+
+    @Override
+    public long mTimeNanoSecs() {
+        return layout.st_mtimensec.get(memory);
+    }
+}
diff --git a/src/main/java/jnr/posix/MacOSFileStat64Inode.java b/src/main/java/jnr/posix/MacOSFileStat64Inode.java
new file mode 100644
index 0000000..703830a
--- /dev/null
+++ b/src/main/java/jnr/posix/MacOSFileStat64Inode.java
@@ -0,0 +1,144 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: EPL 2.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Eclipse Public
+ * License Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * 
+ *  
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+/**
+ * $Id: $
+ */
+
+package jnr.posix;
+
+import jnr.ffi.StructLayout;
+
+/**
+ * An alternate stat layout when running with _DARWIN_FEATURE_64_BIT_INODE, which appears to be the default on M1.
+ */
+public final class MacOSFileStat64Inode extends BaseFileStat implements NanosecondFileStat {
+    public static class Layout64Inode extends StructLayout {
+
+        public Layout64Inode(jnr.ffi.Runtime runtime) {
+            super(runtime);
+        }
+
+        public final class time_t extends SignedLong {
+        }
+        public final Signed32 st_dev = new Signed32();
+        public final Signed16 st_mode = new Signed16();
+        public final Signed16 st_nlink = new Signed16();
+        public final Signed64 st_ino = new Signed64();
+        public final Signed32 st_uid = new Signed32();
+        public final Signed32 st_gid = new Signed32();
+        public final Signed32 st_rdev = new Signed32();
+        public final time_t st_atime = new time_t();
+        public final SignedLong st_atimensec = new SignedLong();
+        public final time_t st_mtime = new time_t();
+        public final SignedLong st_mtimensec = new SignedLong();
+        public final time_t st_ctime = new time_t();
+        public final SignedLong st_ctimensec = new SignedLong();
+        public final time_t st_birthtime = new time_t();
+        public final SignedLong st_birthtimensec = new SignedLong();
+        public final Signed64 st_size = new Signed64();
+        public final Signed64 st_blocks = new Signed64();
+        public final Signed32 st_blksize = new Signed32();
+        public final Signed32 st_flags = new Signed32();
+        public final Signed32 st_gen = new Signed32();
+        public final Signed32 st_lspare = new Signed32();
+        public final Signed64 st_qspare0 = new Signed64();
+        public final Signed64 st_qspare1 = new Signed64();
+    }
+    private static final Layout64Inode layout = new Layout64Inode(jnr.ffi.Runtime.getSystemRuntime());
+
+    public MacOSFileStat64Inode(MacOSPOSIX posix) {
+        super(posix, layout);
+    }
+
+    public long atime() {
+        return layout.st_atime.get(memory);
+    }
+
+    public long blocks() {
+        return layout.st_blocks.get(memory);
+    }
+
+    public long blockSize() {
+        return layout.st_blksize.get(memory);
+    }
+
+    public long ctime() {
+        return layout.st_ctime.get(memory);
+    }
+
+    public long dev() {
+        return layout.st_dev.get(memory);
+    }
+
+    public int gid() {
+        return layout.st_gid.get(memory);
+    }
+
+    public long ino() {
+        return layout.st_ino.get(memory);
+    }
+
+    public int mode() {
+        return layout.st_mode.get(memory) & 0xffff;
+    }
+
+    public long mtime() {
+        return layout.st_mtime.get(memory);
+    }
+
+    public int nlink() {
+        return layout.st_nlink.get(memory);
+    }
+
+    public long rdev() {
+        return layout.st_rdev.get(memory);
+    }
+
+    public long st_size() {
+        return layout.st_size.get(memory);
+    }
+
+    public int uid() {
+        return layout.st_uid.get(memory);
+    }
+
+    @Override
+    public long aTimeNanoSecs() {
+        return layout.st_atimensec.get(memory);
+    }
+
+    @Override
+    public long cTimeNanoSecs() {
+        return layout.st_ctimensec.get(memory);
+    }
+
+    @Override
+    public long mTimeNanoSecs() {
+        return layout.st_mtimensec.get(memory);
+    }
+}
diff --git a/src/main/java/jnr/posix/MacOSMsgHdr.java b/src/main/java/jnr/posix/MacOSMsgHdr.java
new file mode 100644
index 0000000..b8a1f36
--- /dev/null
+++ b/src/main/java/jnr/posix/MacOSMsgHdr.java
@@ -0,0 +1,151 @@
+package jnr.posix;
+
+import jnr.ffi.Pointer;
+import jnr.ffi.Runtime;
+import jnr.ffi.StructLayout;
+
+import java.nio.ByteBuffer;
+
+/**
+ * @author Bob McWhirter
+ */
+class MacOSMsgHdr extends BaseMsgHdr {
+
+    public static class Layout extends StructLayout {
+        protected Layout(Runtime runtime) {
+            super(runtime);
+        }
+
+        public final Pointer msg_name = new Pointer();
+        public final socklen_t msg_namelen = new socklen_t();
+        public final Pointer msg_iov = new Pointer();
+        public final Signed32 msg_iovlen = new Signed32();
+        public final Pointer msg_control = new Pointer();
+        public final socklen_t msg_controllen = new socklen_t();
+        public final Signed32 msg_flags = new Signed32();
+    }
+
+    private static final Layout layout = new Layout(jnr.ffi.Runtime.getSystemRuntime());
+
+    protected MacOSMsgHdr(NativePOSIX posix) {
+        super(posix, layout);
+        setName(null);
+    }
+
+    CmsgHdr allocateCmsgHdrInternal(NativePOSIX posix, Pointer pointer, int len) {
+        if (len > 0) {
+            return new MacOSCmsgHdr(posix, pointer, len);
+        } else {
+            return new MacOSCmsgHdr(posix, pointer);
+        }
+    }
+
+    @Override
+    void setControlPointer(Pointer control) {
+        layout.msg_control.set(this.memory, control);
+    }
+
+    @Override
+    void setControlLen(int len) {
+        layout.msg_controllen.set(this.memory, len);
+    }
+
+    public String toString() {
+        StringBuffer buf = new StringBuffer();
+        buf.append("msghdr {\n");
+        buf.append("  msg_name=").append(getName()).append(",\n");
+        buf.append("  msg_namelen=").append(getNameLen()).append(",\n");
+
+        buf.append("  msg_iov=[\n");
+        Pointer iovp = layout.msg_iov.get(this.memory);
+
+        int numIov = getIovLen();
+        for (int i = 0; i < numIov; ++i) {
+            Pointer eachp = iovp.slice(i * BaseIovec.layout.size());
+            buf.append(new BaseIovec(posix, eachp).toString("    "));
+            if (i < (numIov - 1)) {
+                buf.append(",\n");
+            } else {
+                buf.append("\n");
+            }
+        }
+        buf.append("  ],\n");
+
+        buf.append("  msg_control=[\n");
+
+        CmsgHdr[] controls = getControls();
+        for (int i = 0; i < controls.length; ++i) {
+            buf.append(((MacOSCmsgHdr) controls[i]).toString("    "));
+            if (i < controls.length - 1) {
+                buf.append(",\n");
+            } else {
+                buf.append("\n");
+            }
+        }
+        buf.append("  ],\n");
+        buf.append("  msg_controllen=").append(layout.msg_controllen.get(this.memory)).append("\n");
+
+        buf.append("  msg_iovlen=").append(getIovLen()).append(",\n");
+        buf.append("  msg_flags=").append(getFlags()).append(",\n");
+        buf.append("}");
+        return buf.toString();
+    }
+
+    @Override
+    void setNamePointer(Pointer name) {
+        layout.msg_name.set( this.memory, name );
+    }
+
+    @Override
+    Pointer getNamePointer() {
+        return layout.msg_name.get( this.memory );
+    }
+
+
+    @Override
+    void setNameLen(int len) {
+        layout.msg_namelen.set(this.memory, len);
+    }
+
+    @Override
+    int getNameLen() {
+        return (int) layout.msg_namelen.get(this.memory);
+    }
+
+    @Override
+    void setIovPointer(Pointer iov) {
+        layout.msg_iov.set(this.memory, iov);
+    }
+
+    @Override
+    Pointer getIovPointer() {
+        return layout.msg_iov.get( this.memory );
+    }
+
+    @Override
+    void setIovLen(int len) {
+        layout.msg_iovlen.set(this.memory, len);
+    }
+
+    @Override
+    int getIovLen() {
+        return layout.msg_iovlen.get(this.memory);
+    }
+
+    @Override
+    Pointer getControlPointer() {
+        return layout.msg_control.get(this.memory);
+    }
+
+    public int getControlLen() {
+        return (int) layout.msg_controllen.get(this.memory);
+    }
+
+    public void setFlags(int flags) {
+        layout.msg_flags.set(this.memory, flags);
+    }
+
+    public int getFlags() {
+        return layout.msg_flags.get(this.memory);
+    }
+}
diff --git a/src/main/java/jnr/posix/MacOSPOSIX.java b/src/main/java/jnr/posix/MacOSPOSIX.java
new file mode 100644
index 0000000..d505317
--- /dev/null
+++ b/src/main/java/jnr/posix/MacOSPOSIX.java
@@ -0,0 +1,75 @@
+package jnr.posix;
+
+import java.nio.ByteBuffer;
+import jnr.constants.platform.Confstr;
+import jnr.constants.platform.Pathconf;
+import jnr.constants.platform.Sysconf;
+import jnr.ffi.*;
+import jnr.ffi.mapper.FromNativeContext;
+
+
+final class MacOSPOSIX extends BaseNativePOSIX {
+
+    private final NSGetEnviron environ;
+
+    MacOSPOSIX(LibCProvider libcProvider, POSIXHandler handler) {
+        super(libcProvider, handler);
+
+        final LibraryLoader<NSGetEnviron> loader = LibraryLoader.create(NSGetEnviron.class);
+        loader.library("libSystem.B.dylib");
+        environ = loader.load();
+    }
+
+    public FileStat allocateStat() {
+        if (Platform.getNativePlatform().getCPU() == Platform.CPU.AARCH64) {
+            return new MacOSFileStat64Inode(this);
+        }
+
+        return new MacOSFileStat(this);
+    }
+
+    public MsgHdr allocateMsgHdr() {
+        return new MacOSMsgHdr(this);
+    }
+
+    @Override
+    public Pointer allocatePosixSpawnFileActions() {
+        return Memory.allocateDirect(getRuntime(), 8);
+    }
+
+    @Override
+    public Pointer allocatePosixSpawnattr() {
+        return Memory.allocateDirect(getRuntime(), 8);
+    }
+
+    public SocketMacros socketMacros() {
+        return MacOSSocketMacros.INSTANCE;
+    }
+
+    public long sysconf(Sysconf name) {
+        return libc().sysconf(name);
+    }
+
+    public int confstr(Confstr name, ByteBuffer buf, int len) {
+        return libc().confstr(name, buf, len);
+    }
+
+    public int fpathconf(int fd, Pathconf name) {
+        return libc().fpathconf(fd, name);
+    }
+
+    public Times times() {
+        return NativeTimes.times(this);
+    }
+
+    @Override
+    public Pointer environ() {
+        return environ._NSGetEnviron().getPointer(0);
+    }
+
+    public static final PointerConverter PASSWD = new PointerConverter() {
+        public Object fromNative(Object arg, FromNativeContext ctx) {
+            return arg != null ? new MacOSPasswd((Pointer) arg) : null;
+        }
+    };
+}
diff --git a/src/main/java/jnr/posix/MacOSPasswd.java b/src/main/java/jnr/posix/MacOSPasswd.java
new file mode 100644
index 0000000..16f21ee
--- /dev/null
+++ b/src/main/java/jnr/posix/MacOSPasswd.java
@@ -0,0 +1,74 @@
+
+
+package jnr.posix;
+
+
+import jnr.ffi.StructLayout;
+
+/**
+ *
+ */
+public final class MacOSPasswd extends NativePasswd implements Passwd {
+    private static final class Layout extends StructLayout {
+
+        private Layout(jnr.ffi.Runtime runtime) {
+            super(runtime);
+        }
+
+        public final UTF8StringRef pw_name = new UTF8StringRef();   // user name
+        public final UTF8StringRef pw_passwd = new UTF8StringRef(); // password (encrypted)
+        public final Signed32 pw_uid = new Signed32();       // user id
+        public final Signed32 pw_gid = new Signed32();       // user id
+        public final SignedLong pw_change = new SignedLong();    // password change time
+        public final UTF8StringRef pw_class = new UTF8StringRef();  // user access class
+        public final UTF8StringRef pw_gecos = new UTF8StringRef();  // login info
+        public final UTF8StringRef pw_dir = new UTF8StringRef();    // home directory
+        public final UTF8StringRef pw_shell = new UTF8StringRef();  // default shell
+        public final SignedLong pw_expire = new SignedLong();    // account expiration
+    }
+    private static final Layout layout = new Layout(jnr.ffi.Runtime.getSystemRuntime());
+    
+    MacOSPasswd(jnr.ffi.Pointer memory) {
+        super(memory);
+    }
+    
+    public java.lang.String getAccessClass() {
+        return layout.pw_class.get(memory);
+    }
+    
+    public java.lang.String getGECOS() {
+        return layout.pw_gecos.get(memory);
+    }
+    
+    public long getGID() {
+        return layout.pw_gid.get(memory);
+    }
+    
+    public java.lang.String getHome() {
+        return layout.pw_dir.get(memory);
+    }
+    
+    public java.lang.String getLoginName() {
+        return layout.pw_name.get(memory);
+    }
+    
+    public int getPasswdChangeTime() {
+        return layout.pw_change.intValue(memory);
+    }
+    
+    public java.lang.String getPassword() {
+        return layout.pw_passwd.get(memory);
+    }
+    
+    public java.lang.String getShell() {
+        return layout.pw_shell.get(memory);
+    }
+    
+    public long getUID() {
+        return layout.pw_uid.get(memory);
+    }
+    
+    public int getExpire() {
+        return layout.pw_expire.intValue(memory);
+    }
+}
diff --git a/src/main/java/jnr/posix/MacOSSocketMacros.java b/src/main/java/jnr/posix/MacOSSocketMacros.java
new file mode 100644
index 0000000..c33f239
--- /dev/null
+++ b/src/main/java/jnr/posix/MacOSSocketMacros.java
@@ -0,0 +1,27 @@
+package jnr.posix;
+
+import jnr.ffi.Pointer;
+
+/**
+ * @author Bob McWhirter
+ */
+public class MacOSSocketMacros implements SocketMacros {
+
+    public static final SocketMacros INSTANCE = new MacOSSocketMacros();
+
+    public int __DARWIN_ALIGN32(int x) {
+        return ((x + 3) & ~3);
+    }
+
+    public int CMSG_SPACE(int l) {
+        return __DARWIN_ALIGN32(MacOSCmsgHdr.layout.size()) + __DARWIN_ALIGN32(l);
+    }
+
+    public int CMSG_LEN(int l) {
+        return (__DARWIN_ALIGN32(MacOSCmsgHdr.layout.size())) + (l);
+    }
+
+    public Pointer CMSG_DATA(Pointer cmsg) {
+        return cmsg.slice(__DARWIN_ALIGN32(MacOSCmsgHdr.layout.size()));
+    }
+}
diff --git a/src/main/java/jnr/posix/MsgHdr.java b/src/main/java/jnr/posix/MsgHdr.java
new file mode 100644
index 0000000..ffac87e
--- /dev/null
+++ b/src/main/java/jnr/posix/MsgHdr.java
@@ -0,0 +1,25 @@
+package jnr.posix;
+
+import java.nio.ByteBuffer;
+
+/**
+ * @author Bob McWhirter
+ */
+public interface MsgHdr {
+
+    void setName(String name);
+    String getName();
+
+    void setIov(ByteBuffer[] buffers);
+    ByteBuffer[] getIov();
+
+
+    void setFlags(int flags);
+    int getFlags();
+
+    CmsgHdr allocateControl(int dataLength);
+    CmsgHdr[] allocateControls(int[] dataLengths);
+
+    CmsgHdr[] getControls();
+    int getControlLen();
+}
diff --git a/src/main/java/jnr/posix/NSGetEnviron.java b/src/main/java/jnr/posix/NSGetEnviron.java
new file mode 100644
index 0000000..4abe8c4
--- /dev/null
+++ b/src/main/java/jnr/posix/NSGetEnviron.java
@@ -0,0 +1,9 @@
+package jnr.posix;
+
+import jnr.ffi.Pointer;
+
+public interface NSGetEnviron {
+
+    Pointer _NSGetEnviron();
+
+}
diff --git a/src/main/java/jnr/posix/NanosecondFileStat.java b/src/main/java/jnr/posix/NanosecondFileStat.java
new file mode 100644
index 0000000..00f1e96
--- /dev/null
+++ b/src/main/java/jnr/posix/NanosecondFileStat.java
@@ -0,0 +1,12 @@
+package jnr.posix;
+
+/**
+ * Represents the additional nsec resolution on the stat struct in Linux 2.6+.
+ */
+public interface NanosecondFileStat extends FileStat {
+    long aTimeNanoSecs();
+
+    long cTimeNanoSecs();
+
+    long mTimeNanoSecs();
+}
diff --git a/src/main/java/jnr/posix/NativeGroup.java b/src/main/java/jnr/posix/NativeGroup.java
new file mode 100644
index 0000000..964f3b4
--- /dev/null
+++ b/src/main/java/jnr/posix/NativeGroup.java
@@ -0,0 +1,12 @@
+package jnr.posix;
+
+import jnr.ffi.StructLayout;
+
+public abstract class NativeGroup implements Group {
+    protected final jnr.ffi.Runtime runtime;
+    protected final StructLayout structLayout;
+    protected NativeGroup(jnr.ffi.Runtime runtime, StructLayout structLayout) {
+        this.runtime = runtime;
+        this.structLayout = structLayout;
+    }
+}
diff --git a/src/main/java/jnr/posix/NativePOSIX.java b/src/main/java/jnr/posix/NativePOSIX.java
new file mode 100644
index 0000000..6c32fc5
--- /dev/null
+++ b/src/main/java/jnr/posix/NativePOSIX.java
@@ -0,0 +1,26 @@
+package jnr.posix;
+
+import jnr.ffi.Memory;
+import jnr.ffi.Pointer;
+
+/**
+ *
+ */
+public abstract class NativePOSIX implements POSIX {
+
+
+    jnr.ffi.Runtime getRuntime() {
+        return jnr.ffi.Runtime.getRuntime(libc());
+    }
+
+    public abstract SocketMacros socketMacros();
+
+    public Pointer allocatePosixSpawnFileActions() {
+        return Memory.allocateDirect(getRuntime(), 128);
+    }
+
+    public Pointer allocatePosixSpawnattr() {
+        return Memory.allocateDirect(getRuntime(), 128);
+    }
+
+}
diff --git a/src/main/java/jnr/posix/NativePasswd.java b/src/main/java/jnr/posix/NativePasswd.java
new file mode 100644
index 0000000..49a7dd4
--- /dev/null
+++ b/src/main/java/jnr/posix/NativePasswd.java
@@ -0,0 +1,12 @@
+package jnr.posix;
+
+import jnr.ffi.Pointer;
+
+
+public abstract class NativePasswd implements Passwd {
+    protected final Pointer memory;
+
+    NativePasswd(jnr.ffi.Pointer pointer) {
+        this.memory = pointer;
+    }
+}
diff --git a/src/main/java/jnr/posix/NativeTimes.java b/src/main/java/jnr/posix/NativeTimes.java
new file mode 100644
index 0000000..68a4252
--- /dev/null
+++ b/src/main/java/jnr/posix/NativeTimes.java
@@ -0,0 +1,51 @@
+package jnr.posix;
+
+import jnr.constants.platform.Errno;
+import jnr.ffi.LastError;
+import jnr.ffi.Memory;
+import jnr.ffi.Pointer;
+import jnr.ffi.StructLayout;
+
+/**
+ *
+ */
+public final class NativeTimes implements Times {
+    static final class Layout extends StructLayout {
+        public final clock_t tms_utime = new clock_t();
+        public final clock_t tms_stime = new clock_t();
+        public final clock_t tms_cutime = new clock_t();
+        public final clock_t tms_cstime = new clock_t();
+
+        Layout(jnr.ffi.Runtime runtime) {
+            super(runtime);
+        }
+    }
+
+    private static final Layout layout = new Layout(jnr.ffi.Runtime.getSystemRuntime());
+    final Pointer memory;
+
+    static NativeTimes times(BaseNativePOSIX posix) {
+        NativeTimes tms = new NativeTimes(posix);
+        return posix.libc().times(tms) == -1 ? null : tms;
+    }
+
+    NativeTimes(NativePOSIX posix) {
+        this.memory = Memory.allocate(posix.getRuntime(), layout.size());
+    }
+
+    public long utime() {
+        return layout.tms_utime.get(memory);
+    }
+
+    public long stime() {
+        return layout.tms_stime.get(memory);
+    }
+
+    public long cutime() {
+        return layout.tms_cutime.get(memory);
+    }
+
+    public long cstime() {
+        return layout.tms_cstime.get(memory);
+    }
+}
diff --git a/src/main/java/jnr/posix/OpenBSDFileStat.java b/src/main/java/jnr/posix/OpenBSDFileStat.java
new file mode 100644
index 0000000..fc4732d
--- /dev/null
+++ b/src/main/java/jnr/posix/OpenBSDFileStat.java
@@ -0,0 +1,138 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: EPL 2.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Eclipse Public
+ * License Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+/**
+ * $Id: $
+ */
+
+package jnr.posix;
+
+import jnr.ffi.StructLayout;
+
+public final class OpenBSDFileStat extends BaseFileStat implements NanosecondFileStat{
+    private static final class Layout extends StructLayout {
+
+        private Layout(jnr.ffi.Runtime runtime) {
+            super(runtime);
+        }
+
+        public final class time_t extends Signed64 {}
+        public final class dev_t extends Signed32 {}
+
+        public final Unsigned32  st_mode = new Unsigned32();
+        public final dev_t  st_dev = new dev_t();
+        public final Unsigned64  st_ino = new Unsigned64();
+        public final Unsigned32  st_nlink = new Unsigned32();
+        public final Unsigned32  st_uid = new Unsigned32();
+        public final Unsigned32  st_gid = new Unsigned32();
+        public final dev_t  st_rdev = new dev_t();
+        public final time_t st_atime = new time_t();
+        public final SignedLong   st_atimensec = new SignedLong();
+        public final time_t st_mtime = new time_t();
+        public final SignedLong   st_mtimensec = new SignedLong();
+        public final time_t st_ctime = new time_t();
+        public final SignedLong   st_ctimensec = new SignedLong();
+        public final Signed64  st_size = new Signed64();
+        public final Signed64  st_blocks = new Signed64();
+        public final Unsigned32  st_blksize = new Unsigned32();
+        public final Unsigned32  st_flags = new Unsigned32();
+        public final Unsigned32  st_gen = new Unsigned32();
+        public final time_t st_birthtime = new time_t();
+        public final SignedLong   st_birthtimensec = new SignedLong();
+    }
+    private static final Layout layout = new Layout(jnr.ffi.Runtime.getSystemRuntime());
+    public OpenBSDFileStat(NativePOSIX posix) {
+        super(posix, layout);
+    }
+    
+    public long atime() {
+        return layout.st_atime.get(memory);
+    }
+
+    public long blocks() {
+        return layout.st_blocks.get(memory);
+    }
+
+    public long blockSize() {
+        return layout.st_blksize.get(memory);
+    }
+
+    public long ctime() {
+        return layout.st_ctime.get(memory);
+    }
+
+    public long dev() {
+        return layout.st_dev.get(memory);
+    }
+
+    public int gid() {
+        return (int) layout.st_gid.get(memory);
+    }
+
+    public long ino() {
+        return layout.st_ino.get(memory);
+    }
+
+    public int mode() {
+        return (int)(layout.st_mode.get(memory) & 0xffff);
+    }
+
+    public long mtime() {
+        return layout.st_mtime.get(memory);
+    }
+
+    public int nlink() {
+        return (int)layout.st_nlink.get(memory);
+    }
+
+    public long rdev() {
+        return layout.st_rdev.get(memory);
+    }
+
+    public long st_size() {
+        return layout.st_size.get(memory);
+    }
+
+    public int uid() {
+        return (int)layout.st_uid.get(memory);
+    }
+
+    @Override
+    public long aTimeNanoSecs() {
+        return layout.st_atimensec.get(memory);
+    }
+
+    @Override
+    public long cTimeNanoSecs() {
+        return layout.st_ctimensec.get(memory);
+    }
+
+    @Override
+    public long mTimeNanoSecs() {
+        return layout.st_mtimensec.get(memory);
+    }
+}
diff --git a/src/main/java/jnr/posix/OpenBSDPOSIX.java b/src/main/java/jnr/posix/OpenBSDPOSIX.java
new file mode 100644
index 0000000..e3ca82e
--- /dev/null
+++ b/src/main/java/jnr/posix/OpenBSDPOSIX.java
@@ -0,0 +1,103 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: EPL 2.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Eclipse Public
+ * License Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+/**
+ * $Id: $
+ */
+
+package jnr.posix;
+
+import java.nio.ByteBuffer;
+import jnr.constants.platform.Confstr;
+import jnr.constants.platform.Pathconf;
+import jnr.constants.platform.Sysconf;
+import jnr.ffi.Memory;
+import jnr.ffi.Struct;
+import jnr.ffi.mapper.FromNativeContext;
+import jnr.ffi.Pointer;
+import jnr.posix.util.MethodName;
+
+final class OpenBSDPOSIX extends BaseNativePOSIX {
+    OpenBSDPOSIX(LibCProvider libc, POSIXHandler handler) {
+        super(libc, handler);
+    }
+    
+    public FileStat allocateStat() {
+        return new OpenBSDFileStat(this);
+    }
+
+    public MsgHdr allocateMsgHdr() {
+        handler.unimplementedError(MethodName.getCallerMethodName());
+        return null;
+    }
+
+    public SocketMacros socketMacros() {
+        handler.unimplementedError(MethodName.getCallerMethodName());
+        return null;
+    }
+
+    public long sysconf(Sysconf name) {
+        return libc().sysconf(name);
+    }
+
+    public int confstr(Confstr name, ByteBuffer buf, int len) {
+        return libc().confstr(name, buf, len);
+    }
+
+    public int fpathconf(int fd, Pathconf name) {
+        return libc().fpathconf(fd, name);
+    }
+
+    public Times times() {
+        return NativeTimes.times(this);
+    }
+    
+    public static final PointerConverter PASSWD = new PointerConverter() {
+        public Object fromNative(Object arg, FromNativeContext ctx) {
+            return arg != null ? new OpenBSDPasswd((Pointer) arg) : null;
+        }
+    };
+
+    @Override
+    public int utimes(String path, long[] atimeval, long[] mtimeval) {
+        Timeval[] times = null;
+        if (atimeval != null && mtimeval != null) {
+            times = Struct.arrayOf(getRuntime(), OpenBSDTimeval.class, 2);
+            times[0].setTime(atimeval);
+            times[1].setTime(mtimeval);
+        }
+        return libc().utimes(path, times);
+    }
+
+    public Pointer allocatePosixSpawnFileActions() {
+        return Memory.allocateDirect(getRuntime(), 8);
+    }
+
+    public Pointer allocatePosixSpawnattr() {
+        return Memory.allocateDirect(getRuntime(), 8);
+    }
+}
diff --git a/src/main/java/jnr/posix/OpenBSDPasswd.java b/src/main/java/jnr/posix/OpenBSDPasswd.java
new file mode 100644
index 0000000..4c61681
--- /dev/null
+++ b/src/main/java/jnr/posix/OpenBSDPasswd.java
@@ -0,0 +1,100 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: EPL 2.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Eclipse Public
+ * License Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+/**
+ * $Id: $
+ */
+
+package jnr.posix;
+
+
+import jnr.ffi.StructLayout;
+
+public class OpenBSDPasswd extends NativePasswd implements Passwd {
+    private static final class Layout extends StructLayout {
+
+        private Layout(jnr.ffi.Runtime runtime) {
+            super(runtime);
+        }
+
+        public final UTF8StringRef pw_name = new UTF8StringRef();   // user name
+        public final UTF8StringRef pw_passwd = new UTF8StringRef(); // password (encrypted)
+        public final Unsigned32 pw_uid = new Unsigned32();       // user id
+        public final Unsigned32 pw_gid = new Unsigned32();       // user id
+        public final Signed64 pw_change = new Signed64();    // password change time
+        public final UTF8StringRef pw_class = new UTF8StringRef();  // user access class
+        public final UTF8StringRef pw_gecos = new UTF8StringRef();  // login info
+        public final UTF8StringRef pw_dir = new UTF8StringRef();    // home directory
+        public final UTF8StringRef pw_shell = new UTF8StringRef();  // default shell
+        public final Signed64 pw_expire = new Signed64();    // account expiration
+    }
+    private static final Layout layout = new Layout(jnr.ffi.Runtime.getSystemRuntime());
+
+    OpenBSDPasswd(jnr.ffi.Pointer memory) {
+        super(memory);
+    }
+
+    public java.lang.String getAccessClass() {
+        return layout.pw_class.get(memory);
+    }
+
+    public java.lang.String getGECOS() {
+        return layout.pw_gecos.get(memory);
+    }
+
+    public long getGID() {
+        return layout.pw_gid.get(memory);
+    }
+
+    public java.lang.String getHome() {
+        return layout.pw_dir.get(memory);
+    }
+
+    public java.lang.String getLoginName() {
+        return layout.pw_name.get(memory);
+    }
+
+    public int getPasswdChangeTime() {
+        return layout.pw_change.intValue(memory);
+    }
+
+    public java.lang.String getPassword() {
+        return layout.pw_passwd.get(memory);
+    }
+
+    public java.lang.String getShell() {
+        return layout.pw_shell.get(memory);
+    }
+
+    public long getUID() {
+        return layout.pw_uid.get(memory);
+    }
+
+    public int getExpire() {
+        return layout.pw_expire.intValue(memory);
+    }
+}
diff --git a/src/main/java/jnr/posix/OpenBSDTimeval.java b/src/main/java/jnr/posix/OpenBSDTimeval.java
new file mode 100644
index 0000000..734d456
--- /dev/null
+++ b/src/main/java/jnr/posix/OpenBSDTimeval.java
@@ -0,0 +1,32 @@
+package jnr.posix;
+
+public final class OpenBSDTimeval extends Timeval {
+    public final Signed64 tv_sec = new Signed64();
+    public final SignedLong tv_usec = new SignedLong();
+
+    public OpenBSDTimeval(jnr.ffi.Runtime runtime) {
+        super(runtime);
+    }
+
+    public void setTime(long[] timeval) {
+        assert timeval.length == 2;
+        tv_sec.set(timeval[0]);
+        tv_usec.set(timeval[1]);
+    }
+
+    public void sec(long sec) {
+        this.tv_sec.set(sec);
+    }
+
+    public void usec(long usec) {
+        this.tv_usec.set(usec);
+    }
+
+    public long sec() {
+        return tv_sec.get();
+    }
+
+    public long usec() {
+        return tv_usec.get();
+    }
+}
diff --git a/src/main/java/jnr/posix/POSIX.java b/src/main/java/jnr/posix/POSIX.java
new file mode 100644
index 0000000..941067d
--- /dev/null
+++ b/src/main/java/jnr/posix/POSIX.java
@@ -0,0 +1,237 @@
+package jnr.posix;
+
+import jnr.constants.platform.Fcntl;
+import jnr.constants.platform.Signal;
+import jnr.constants.platform.Sysconf;
+import jnr.ffi.Pointer;
+import jnr.posix.util.ProcessMaker;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Collection;
+import jnr.constants.platform.Confstr;
+import jnr.constants.platform.Pathconf;
+import jnr.ffi.annotations.Out;
+
+public interface POSIX {
+    CharSequence crypt(CharSequence key, CharSequence salt);
+
+    /**
+     * Call the crypt function with the given key and salt as raw null-terminated byte (C char) strings.
+     *
+     * @param key null-terminated key bytes
+     * @param salt null-terminated salt bytes
+     * @return null-terminated crypted bytes, or null if there was an error
+     */
+    byte[] crypt(byte[] key, byte[] salt);
+
+    FileStat allocateStat();
+    int chmod(String filename, int mode);
+    int fchmod(int fd, int mode);
+    int chown(String filename, int user, int group);
+    int fchown(int fd, int user, int group);
+    /**
+     * Shell expanding and escaping version of exec which handles all the
+     * preparation of a command line or command list.
+     *
+     * @param path the path to execute
+     * @param argv the arguments to pass, with arg0 equal to the desired process name
+     * @return does not return if successful; -1 if failed
+     */
+    int exec(String path, String... argv);
+    
+    /**
+     * Shell expanding and escaping version of exec which handles all the
+     * preparation of a command line or command list.
+     *
+     * @param path the path to execute
+     * @param argv the arguments to pass, with arg0 equal to the desired process name
+     * @param envp a set of KEY=VALUE environment strings to set for the new execution
+     * @return does not return if successful; -1 if failed
+     */
+    int exec(String path, String[] argv, String[] envp);
+    
+    int execv(String path, String[] argv);  
+    int execve(String path, String[] argv, String[] envp);    
+    int fork();
+    FileStat fstat(FileDescriptor descriptor);
+    FileStat fstat(int descriptor);
+    int fstat(FileDescriptor descriptor, FileStat stat);
+    int fstat(int fd, FileStat stat);
+    Pointer environ();
+    String getenv(String envName);
+    int getegid();
+    int geteuid();
+    int seteuid(int euid);
+    int getgid();
+    int getdtablesize();
+    String getlogin();
+    int getpgid();
+    int getpgid(int pid);
+    int getpgrp();
+    int getpid();
+    int getppid();
+    int getpriority(int which, int who);
+    Passwd getpwent();
+    Passwd getpwuid(int which);
+    Passwd getpwnam(String which);
+    Group getgrgid(int which);
+    Group getgrnam(String which);
+    Group getgrent();
+    int endgrent();
+    int setgrent();
+    int endpwent();
+    int setpwent();
+    int getuid();
+    int getrlimit(int resource, RLimit rlim);
+    int getrlimit(int resource, Pointer rlim);
+    RLimit getrlimit(int resource);
+    int setrlimit(int resource, RLimit rlim);
+    int setrlimit(int resource, Pointer rlim);
+    int setrlimit(int resource, long rlimCur, long rlimMax);
+    boolean isatty(FileDescriptor descriptor);
+    int isatty(int descriptor);
+    int kill(int pid, int signal);
+    int kill(long pid, int signal);
+    SignalHandler signal(Signal sig, SignalHandler handler);
+    int raise(int sig);
+    int lchmod(String filename, int mode);
+    int lchown(String filename, int user, int group);
+    int link(String oldpath,String newpath);
+    FileStat lstat(String path);
+    int lstat(String path, FileStat stat);
+    int mkdir(String path, int mode);
+    String readlink(String path) throws IOException;
+    int readlink(CharSequence path, byte[] buf, int bufsize);
+    int readlink(CharSequence path, ByteBuffer buf, int bufsize);
+    int readlink(CharSequence path, Pointer bufPtr, int bufsize);
+    int rmdir(String path);
+    int setenv(String envName, String envValue, int overwrite); // 0 no !0 yes
+    int setsid();
+    int setgid(int gid);
+    int setegid(int egid);
+    int setpgid(int pid, int pgid);
+    int setpgrp(int pid, int pgrp);
+    int setpriority(int which, int who, int prio);
+    int setuid(int uid);
+    FileStat stat(String path);
+    int stat(String path, FileStat stat);
+    int symlink(String oldpath,String newpath);
+    int umask(int mask);
+    int unsetenv(String envName);
+    int utimes(String path, long[] atimeval, long[] mtimeval);
+    int utimes(String path, Pointer times);
+    int futimes(int fd, long[] atimeval, long[] mtimeval);
+    int lutimes(String path, long[] atimeval, long[] mtimeval);
+    int utimensat(int dirfd, String path, long[] atimespec, long[] mtimespec, int flag);
+    int utimensat(int dirfd, String path, Pointer times, int flag);
+    int futimens(int fd, long[] atimespec, long[] mtimespec);
+    int futimens(int fd, Pointer times);
+    int waitpid(int pid, int[] status, int flags);
+    int waitpid(long pid, int[] status, int flags);
+    int wait(int[] status);
+    int errno();
+    void errno(int value);
+    String strerror(int code);
+    int chdir(String path);
+    boolean isNative();
+    /**
+     * Returns null if isNative returns false.
+     *
+     * @return the LibC implementation for this POSIX
+     */
+    LibC libc();
+    ProcessMaker newProcessMaker(String... command);
+    ProcessMaker newProcessMaker();
+
+    public long sysconf(Sysconf name);
+
+    public int confstr(Confstr name, @Out ByteBuffer buf, int len);
+
+    public int fpathconf(int fd, Pathconf name);
+
+    public Times times();
+
+    public long posix_spawnp(String path, Collection<? extends SpawnFileAction> fileActions,
+                            Collection<? extends CharSequence> argv, Collection<? extends CharSequence> envp);
+
+    public long posix_spawnp(String path, Collection<? extends SpawnFileAction> fileActions,
+                             Collection<? extends SpawnAttribute> spawnAttributes,
+                             Collection<? extends CharSequence> argv, Collection<? extends CharSequence> envp);
+    
+    public int flock(int fd, int operation);
+
+    int dup(int fd);
+
+    int dup2(int oldFd, int newFd);
+
+    int fcntlInt(int fd, Fcntl fcntlConst, int arg);
+    int fcntl(int fd, Fcntl fcntlConst, int arg);
+    int fcntl(int fd, Fcntl fcntlConst);
+    int access(CharSequence path, int amode);
+    int close(int fd);
+    int unlink(CharSequence path);
+    int open(CharSequence path, int flags, int perm);
+
+    long read(int fd, byte[] buf, long n);
+    long write(int fd, byte[] buf, long n);
+    long read(int fd, ByteBuffer buf, long n);
+    long write(int fd, ByteBuffer buf, long n);
+    long pread(int fd, byte[] buf, long n, long offset);
+    long pwrite(int fd, byte[] buf, long n, long offset);
+    long pread(int fd, ByteBuffer buf, long n, long offset);
+    long pwrite(int fd, ByteBuffer buf, long n, long offset);
+
+    int read(int fd, byte[] buf, int n);
+    int write(int fd, byte[] buf, int n);
+    int read(int fd, ByteBuffer buf, int n);
+    int write(int fd, ByteBuffer buf, int n);
+    int pread(int fd, byte[] buf, int n, int offset);
+    int pwrite(int fd, byte[] buf, int n, int offset);
+    int pread(int fd, ByteBuffer buf, int n, int offset);
+    int pwrite(int fd, ByteBuffer buf, int n, int offset);
+
+    int lseek(int fd, long offset, int whence);
+    long lseekLong(int fd, long offset, int whence);
+    int pipe(int[] fds);
+    int truncate(CharSequence path, long length);
+    int ftruncate(int fd, long offset);
+    int rename(CharSequence oldName, CharSequence newName);
+    String getcwd();
+    String gethostname();
+
+    int socketpair(int domain, int type, int protocol, int[] fds);
+    int sendmsg(int socket, MsgHdr message, int flags);
+    int recvmsg(int socket, MsgHdr message, int flags);
+
+    MsgHdr allocateMsgHdr();
+
+    /**
+     * fcntl(2)
+     *
+     * @deprecated This version does not pass args because jnr-ffi does not support variadic invocation.
+     * @see jnr.posix.POSIX#fcntlInt(int, jnr.constants.platform.Fcntl, int)
+     *
+     * @param fd the file descriptor on which to act
+     * @param fcntlConst the {@link Fcntl} enum value for the flag to set
+     * @param arg arguments for the flag or null if none
+     * @return 0 if success, -1 if error
+     */
+    @Deprecated
+    int fcntl(int fd, Fcntl fcntlConst, int... arg);
+    int fsync(int fd);
+    int fdatasync(int fd);
+    int mkfifo(String filename, int mode);
+
+    int daemon(int nochdir, int noclose);
+
+    long[] getgroups();
+    int getgroups(int size, int[] groups);
+
+    String nl_langinfo(int item);
+    String setlocale(int category, String locale);
+
+    Timeval allocateTimeval();
+    int gettimeofday(Timeval tv);
+}
diff --git a/src/main/java/jnr/posix/POSIXFactory.java b/src/main/java/jnr/posix/POSIXFactory.java
new file mode 100644
index 0000000..a1ea703
--- /dev/null
+++ b/src/main/java/jnr/posix/POSIXFactory.java
@@ -0,0 +1,342 @@
+package jnr.posix;
+
+import jnr.ffi.Library;
+import jnr.ffi.LibraryLoader;
+import jnr.ffi.LibraryOption;
+import jnr.ffi.Struct;
+
+import java.util.Collections;
+import java.util.HashMap;
+
+import jnr.ffi.mapper.FunctionMapper;
+import jnr.posix.util.DefaultPOSIXHandler;
+import jnr.posix.util.Platform;
+
+import java.util.Map;
+
+public class POSIXFactory {
+    // Weird inner-class resolution problem work-around FIXME: JRUBY-5889.  Someone fix JAFFL!
+    private static final Class<Struct> BOGUS_HACK = Struct.class;
+    public static final jnr.ffi.Platform NATIVE_PLATFORM = jnr.ffi.Platform.getNativePlatform();
+    public static final String STANDARD_C_LIBRARY_NAME = NATIVE_PLATFORM.getStandardCLibraryName();
+
+    /**
+     * Get a POSIX instance. If useNativePosix is true, this works just like
+     * POSIXFactory#getPOSIX(). If useNativePosix is false, this works like
+     * POSIXFactory#getJavaPOSIX()
+     *
+     * @param handler a POSIXHandler implementation
+     * @param useNativePOSIX whether to attempt to use native code for better functionality
+     * @return a POSIX implementation, attempting to use native code if useNativePosix is true
+     */
+    public static POSIX getPOSIX(POSIXHandler handler, boolean useNativePOSIX) {
+        return new LazyPOSIX(handler, useNativePOSIX);
+    }
+
+    /**
+     * This will use {@link DefaultPOSIXHandler} and the native POSIX implementation,
+     * falling back on the pure-Java implementation if native support is not available.
+     *
+     * @return a POSIX implementation, native if possible and pure-Java otherwise.
+     */
+    public static POSIX getPOSIX() {
+        return getPOSIX(new DefaultPOSIXHandler(), true);
+    }
+
+    /**
+     * Get a pure-Java POSIX instance. Functionality will be limited to that which can
+     * be provided by pure-Java/JDK features or shelling out to external commands.
+     *
+     * @param handler a POSIXHandler implementation
+     * @return a pure-Java POSIX implementation
+     */
+    public static POSIX getJavaPOSIX(POSIXHandler handler) {
+        return new JavaPOSIX(handler);
+    }
+
+    /**
+     * Get a pure-Java POSIX instance. Functionality will be limited to that which can
+     * be provided by pure-Java/JDK features or shelling out to external commands.
+     *
+     * @return a pure-Java POSIX implementation
+     */
+    public static POSIX getJavaPOSIX() {
+        return getJavaPOSIX(new DefaultPOSIXHandler());
+    }
+
+    /**
+     * Get a POSIX instance. If a true native implementation can't be loaded, allow that
+     * error to propagate rather than falling back on the pure-Java version.
+     *
+     * @param handler a POSIXHandler implementation
+     * @return a native POSIX implementation, raising errors if the native version can't load
+     */
+    public static POSIX getNativePOSIX(POSIXHandler handler) {
+        return loadNativePOSIX(handler);
+    }
+
+    /**
+     * Get a POSIX instance. If a true native implementation can't be loaded, allow that
+     * error to propagate rather than falling back on the pure-Java version.
+     *
+     * @return a native POSIX implementation, raising errors if the native version can't load
+     */
+    public static POSIX getNativePOSIX() {
+        return getNativePOSIX(new DefaultPOSIXHandler());
+    }
+
+    static POSIX loadPOSIX(POSIXHandler handler, boolean useNativePOSIX) {
+        POSIX posix = null;
+
+        if (useNativePOSIX) {
+            try {
+                posix = loadNativePOSIX(handler);
+                posix = posix != null ? new CheckedPOSIX(posix, handler) : null;
+                // ENEBO: Should printing be done through a handler+log method?
+                if (handler.isVerbose()) {
+                    if (posix != null) {
+                        System.err.println("Successfully loaded native POSIX impl.");
+                    } else {
+                        System.err.println("Failed to load native POSIX impl; falling back on Java impl. Unsupported OS.");
+                    }
+                }
+            } catch (Throwable t) {
+                if (handler.isVerbose()) {
+                    System.err.println("Failed to load native POSIX impl; falling back on Java impl. Stacktrace follows.");
+                    t.printStackTrace();
+                }
+            }
+        }
+
+        if (posix == null) {
+            posix = getJavaPOSIX(handler);
+        }
+
+        return posix;
+    }
+    
+    private static POSIX loadNativePOSIX(POSIXHandler handler) {
+        switch (NATIVE_PLATFORM.getOS()) {
+            case DARWIN:
+                return loadMacOSPOSIX(handler);
+
+            case LINUX:
+                return loadLinuxPOSIX(handler);
+
+            case FREEBSD:
+                return loadFreeBSDPOSIX(handler);
+
+            case DRAGONFLY:
+                return loadDragonFlyPOSIX(handler);
+            
+            case OPENBSD:
+                return loadOpenBSDPOSIX(handler);
+
+            case SOLARIS:
+                return loadSolarisPOSIX(handler);
+            
+            case AIX:
+                return loadAixPOSIX(handler);
+            
+            case WINDOWS:
+                return loadWindowsPOSIX(handler);
+        }
+
+        return null;
+    }
+
+    public static POSIX loadLinuxPOSIX(POSIXHandler handler) {
+        return new LinuxPOSIX(DefaultLibCProvider.INSTANCE, handler);
+    }
+
+    public static POSIX loadMacOSPOSIX(POSIXHandler handler) {
+        return new MacOSPOSIX(DefaultLibCProvider.INSTANCE, handler);
+    }
+
+    public static POSIX loadSolarisPOSIX(POSIXHandler handler) {
+        return new SolarisPOSIX(DefaultLibCProvider.INSTANCE, handler);
+    }
+
+    public static POSIX loadFreeBSDPOSIX(POSIXHandler handler) {
+        return new FreeBSDPOSIX(DefaultLibCProvider.INSTANCE, handler);
+    }
+
+    public static POSIX loadDragonFlyPOSIX(POSIXHandler handler) {
+        return new DragonFlyPOSIX(DefaultLibCProvider.INSTANCE, handler);
+    }
+
+    public static POSIX loadOpenBSDPOSIX(POSIXHandler handler) {
+        return new OpenBSDPOSIX(DefaultLibCProvider.INSTANCE, handler);
+    }
+
+    public static POSIX loadWindowsPOSIX(POSIXHandler handler) {
+        return new WindowsPOSIX(DefaultLibCProvider.INSTANCE, handler);
+    }
+
+    public static POSIX loadAixPOSIX(POSIXHandler handler) {
+        return new AixPOSIX(DefaultLibCProvider.INSTANCE, handler);
+    }
+    
+    private static String[] libraries() {
+        switch (NATIVE_PLATFORM.getOS()) {
+            case LINUX:
+                return new String[] {STANDARD_C_LIBRARY_NAME};
+
+            case SOLARIS:
+                return new String[] { "socket", "nsl", STANDARD_C_LIBRARY_NAME};
+
+            case DRAGONFLY:
+            case FREEBSD:
+            case NETBSD:
+                return new String[] {STANDARD_C_LIBRARY_NAME};
+            
+            case AIX:
+                return jnr.ffi.Runtime.getSystemRuntime().addressSize() == 4
+                    ? new String[] { "libc.a(shr.o)" }
+                    : new String[] { "libc.a(shr_64.o)" };
+            
+            case WINDOWS:
+                return new String[] { "msvcrt", "kernel32" };
+
+            default:
+                return new String[] {STANDARD_C_LIBRARY_NAME};
+        }
+    }
+    
+    private static Class<? extends LibC> libraryInterface() {
+        switch (NATIVE_PLATFORM.getOS()) {
+            case LINUX:
+                return LinuxLibC.class;
+            
+            case AIX:
+                return AixLibC.class;
+            
+            case SOLARIS:
+                return SolarisLibC.class;
+
+            case WINDOWS:
+                return WindowsLibC.class;
+
+            default:
+                return UnixLibC.class;
+        }
+    }
+
+    private static FunctionMapper functionMapper() {
+        switch (NATIVE_PLATFORM.getOS()) {
+            case AIX:
+                return new SimpleFunctionMapper.Builder()
+                        .map("stat", "stat64x")
+                        .map("fstat", "fstat64x")
+                        .map("lstat", "lstat64x")
+                        .map("stat64", "stat64x")
+                        .map("fstat64", "fstat64x")
+                        .map("lstat64", "lstat64x")
+                        .build();
+            
+            case WINDOWS:
+                return new SimpleFunctionMapper.Builder()
+                        .map("getpid", "_getpid")
+                        .map("chmod", "_chmod")
+                        .map("fstat", "_fstat64")
+                        .map("stat", "_stat64")
+                        .map("umask", "_umask")
+                        .map("isatty", "_isatty")
+                        .map("read", "_read")
+                        .map("write", "_write")
+                        .map("close", "_close")
+                        .map("getcwd", "_getcwd")
+                        .map("unlink", "_unlink")
+                        .map("access", "_access")
+                        .map("open", "_open")
+                        .map("dup", "_dup")
+                        .map("dup2", "_dup2")
+                        .map("lseek", "_lseek")
+                        .map("ftruncate", "_chsize")
+                        .build();
+            
+            case SOLARIS:
+                return Platform.IS_32_BIT 
+                    ? new SimpleFunctionMapper.Builder()
+                        .map("stat", "stat64")
+                        .map("fstat", "fstat64")
+                        .map("lstat", "lstat64")
+                        .build()
+                    : null;
+            default:
+                return null;
+        }
+    }
+    
+    private static Map<LibraryOption, Object> options() {
+        Map<LibraryOption, Object> options = new HashMap<LibraryOption, Object>();
+        
+        FunctionMapper functionMapper = functionMapper();
+        if (functionMapper != null) {
+            options.put(LibraryOption.FunctionMapper, functionMapper);
+        }
+
+        options.put(LibraryOption.TypeMapper, POSIXTypeMapper.INSTANCE);
+        options.put(LibraryOption.LoadNow, Boolean.TRUE);
+        
+        return Collections.unmodifiableMap(options);
+    }
+
+    private static final class DefaultLibCProvider implements LibCProvider {
+        public static final LibCProvider INSTANCE = new DefaultLibCProvider();
+
+        private static final class SingletonHolder {
+            public static LibC libc;
+            public static Crypt crypt;
+            static {
+                LibraryLoader<? extends LibC> libcLoader = LibraryLoader.create(libraryInterface());
+
+                // always do a default search
+                libcLoader.searchDefault();
+
+                for (String library : libraries()) {
+                    libcLoader.library(library);
+                }
+
+                for (Map.Entry<LibraryOption, Object> entry : options().entrySet()) {
+                    libcLoader.option(entry.getKey(), entry.getValue());
+                }
+
+                libcLoader.failImmediately();
+
+                libc = libcLoader.load();
+
+                Crypt c = null;
+
+                // FIXME: This is kinda gross but there's no way to tell jnr-ffi that some libraries are ok to fail
+                // See jruby/jruby#5447.
+                try {
+                    LibraryLoader<Crypt> loader = LibraryLoader.create(Crypt.class).failImmediately();
+                    c = loader.load("libcrypt.so.1");
+                } catch (UnsatisfiedLinkError ule) {
+                    try {
+                        LibraryLoader<Crypt> loader = LibraryLoader.create(Crypt.class).failImmediately();
+                        c = loader.load("crypt");
+                    } catch (UnsatisfiedLinkError ule2) {
+                        try {
+                            LibraryLoader<Crypt> loader = LibraryLoader.create(Crypt.class).failImmediately();
+                            c = loader.load(STANDARD_C_LIBRARY_NAME);
+                        } catch (UnsatisfiedLinkError ule3) {
+                            // TODO: log somewhere? Warning?
+                        }
+                    }
+                }
+
+                crypt = c;
+            }
+        }
+
+        public final LibC getLibC() {
+            return SingletonHolder.libc;
+        }
+
+        public final Crypt getCrypt() {
+            return SingletonHolder.crypt;
+        }
+    }
+}
diff --git a/src/main/java/jnr/posix/POSIXFunctionMapper.java b/src/main/java/jnr/posix/POSIXFunctionMapper.java
new file mode 100644
index 0000000..94d30bf
--- /dev/null
+++ b/src/main/java/jnr/posix/POSIXFunctionMapper.java
@@ -0,0 +1,28 @@
+
+package jnr.posix;
+
+import jnr.ffi.mapper.FunctionMapper;
+
+/**
+ * No longer used.  It used to map function names from libc names to
+ * msvcrt names.
+ *
+ * @deprecated Use SimpleFunctionMapper instead.
+ */
+@Deprecated
+final class POSIXFunctionMapper implements FunctionMapper {
+    public static final FunctionMapper INSTANCE = new POSIXFunctionMapper();
+
+    private POSIXFunctionMapper() {}
+  
+    public String mapFunctionName(String name, Context ctx) {
+        if (ctx.getLibrary().getName().equals("msvcrt")) {
+            // FIXME: We should either always _ name for msvcrt or get good list of _ methods
+            if (name.equals("getpid") || name.equals("chmod")) {
+                name = "_" + name;
+            }
+        }
+        return name;
+    }
+
+}
diff --git a/src/main/java/jnr/posix/POSIXHandler.java b/src/main/java/jnr/posix/POSIXHandler.java
new file mode 100644
index 0000000..46fb4c0
--- /dev/null
+++ b/src/main/java/jnr/posix/POSIXHandler.java
@@ -0,0 +1,81 @@
+package jnr.posix;
+
+import jnr.constants.platform.Errno;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.PrintStream;
+
+/**
+ * The POSIXHandler class allows you do implement the runtime-specific behavior you need in
+ * such a way that it is insulated from the implementation of the POSIX library.  Implementing
+ * each of the methods in this interface should give you are working POSIX implementation. 
+ *
+ */
+public interface POSIXHandler {
+    public enum WARNING_ID {
+        DUMMY_VALUE_USED("DUMMY_VALUE_USED");
+        
+        private String messageID;
+
+        WARNING_ID(String messageID) {
+            this.messageID = messageID;
+        }
+    }
+    public void error(Errno error, String extraData);
+    public void error(Errno error, String methodName, String extraData);
+    
+    /**
+     * Specify that posix method is unimplemented.  In JRuby we generate an
+     * exception with this.
+     *
+     * @param methodName the POSIX method that failed
+     */
+    public void unimplementedError(String methodName);
+    
+    public void warn(WARNING_ID id, String message, Object... data);
+    
+    /**
+     * @return should we provide verbose output about POSIX activities
+     */
+    public boolean isVerbose();
+
+    /**
+     * @return current working directory of your runtime.
+     */
+    public File getCurrentWorkingDirectory();
+    
+    /**
+     * @return current set of environment variables of your runtime.
+     */
+    public String[] getEnv();
+    
+    /**
+     * @return your runtime's current input stream
+     */
+    public InputStream getInputStream();
+    
+    /**
+     * @return your runtime's current output stream
+     */
+    public PrintStream getOutputStream();
+    
+    /**
+     * Get your runtime's process ID.  This is only intended for non-native POSIX support (e.g.
+     * environments where JNA cannot load or security restricted environments).  In JRuby we
+     * found a number of packages which would rather have some identity for the runtime than
+     * nothing.
+     * 
+     * Note: If you do not want this to work you impl can just call {@link #unimplementedError(String)}.
+     *
+     * @return your runtime's process ID
+     */
+    public int getPID();
+    
+    /**
+     * Get your runtime's current ErrorStream
+     *
+     * @return your runtime's current error stream
+     */
+    public PrintStream getErrorStream();
+}
diff --git a/src/main/java/jnr/posix/POSIXTypeMapper.java b/src/main/java/jnr/posix/POSIXTypeMapper.java
new file mode 100644
index 0000000..da76d08
--- /dev/null
+++ b/src/main/java/jnr/posix/POSIXTypeMapper.java
@@ -0,0 +1,72 @@
+package jnr.posix;
+
+
+import jnr.constants.Constant;
+import jnr.ffi.mapper.*;
+import jnr.posix.util.Platform;
+
+final class POSIXTypeMapper implements TypeMapper {
+    public static final TypeMapper INSTANCE = new POSIXTypeMapper();
+    
+    private POSIXTypeMapper() {}
+    
+    public FromNativeConverter getFromNativeConverter(Class klazz) {
+        if (Passwd.class.isAssignableFrom(klazz)) {
+            if (Platform.IS_MAC) {
+                return MacOSPOSIX.PASSWD;
+            } else if (Platform.IS_LINUX) {
+                return LinuxPOSIX.PASSWD;
+            } else if (Platform.IS_SOLARIS) {
+                return SolarisPOSIX.PASSWD;
+            } else if (Platform.IS_FREEBSD) {
+                return FreeBSDPOSIX.PASSWD;
+            } else if (Platform.IS_DRAGONFLY) {
+                return DragonFlyPOSIX.PASSWD;
+            } else if (Platform.IS_OPENBSD) {
+                return OpenBSDPOSIX.PASSWD;
+            } else if (Platform.IS_WINDOWS) {
+                return WindowsPOSIX.PASSWD;
+            } else if (jnr.ffi.Platform.getNativePlatform().getOS().equals(jnr.ffi.Platform.OS.AIX)) {
+                return AixPOSIX.PASSWD;
+            }
+            return null;
+        } else if (Group.class.isAssignableFrom(klazz)) {
+            return BaseNativePOSIX.GROUP;
+
+        } else if (HANDLE.class.isAssignableFrom(klazz)) {
+            return HANDLE.Converter;
+        }
+        
+        return null;
+    }
+    
+    public ToNativeConverter getToNativeConverter(Class klazz) {
+        if (FileStat.class.isAssignableFrom(klazz)) {
+            return BaseNativePOSIX.FileStatConverter;
+
+        } else if (NativeTimes.class.isAssignableFrom(klazz)) {
+            return BaseNativePOSIX.TimesConverter;
+
+        } else if (Constant.class.isAssignableFrom(klazz)) {
+            return BaseNativePOSIX.ConstantConverter;
+
+        } else if (WString.class.isAssignableFrom(klazz)) {
+            return WString.Converter;
+
+        } else if (HANDLE.class.isAssignableFrom(klazz)) {
+            return HANDLE.Converter;
+        } else if (MsgHdr.class.isAssignableFrom(klazz)) {
+            return BaseNativePOSIX.MsgHdrConverter;
+        }
+
+        return null;
+    }
+
+    public final ToNativeConverter getToNativeConverter(Class klazz, ToNativeContext context) {
+        return getToNativeConverter(klazz);
+    }
+
+    public final FromNativeConverter getFromNativeConverter(Class klazz, FromNativeContext context) {
+        return getFromNativeConverter(klazz);
+    }
+}
diff --git a/src/main/java/jnr/posix/Passwd.java b/src/main/java/jnr/posix/Passwd.java
new file mode 100644
index 0000000..63cff5f
--- /dev/null
+++ b/src/main/java/jnr/posix/Passwd.java
@@ -0,0 +1,14 @@
+package jnr.posix;
+
+public interface Passwd {
+    public String getLoginName();
+    public String getPassword();
+    public long getUID();
+    public long getGID();
+    public int getPasswdChangeTime();
+    public String getAccessClass();
+    public String getGECOS();
+    public String getHome();
+    public String getShell();
+    public int getExpire();
+}
diff --git a/src/main/java/jnr/posix/RLimit.java b/src/main/java/jnr/posix/RLimit.java
new file mode 100644
index 0000000..0a0018c
--- /dev/null
+++ b/src/main/java/jnr/posix/RLimit.java
@@ -0,0 +1,13 @@
+package jnr.posix;
+
+import jnr.ffi.*;
+
+public abstract class RLimit extends Struct {
+    protected RLimit(jnr.ffi.Runtime runtime) {
+        super(runtime);
+    }
+
+    public abstract void init(long current, long max);
+    public abstract long rlimCur();
+    public abstract long rlimMax();
+}
diff --git a/src/main/java/jnr/posix/SignalHandler.java b/src/main/java/jnr/posix/SignalHandler.java
new file mode 100644
index 0000000..33ab5de
--- /dev/null
+++ b/src/main/java/jnr/posix/SignalHandler.java
@@ -0,0 +1,5 @@
+package jnr.posix;
+
+public interface SignalHandler {
+    public void handle(int signal);
+}
diff --git a/src/main/java/jnr/posix/SimpleFunctionMapper.java b/src/main/java/jnr/posix/SimpleFunctionMapper.java
new file mode 100644
index 0000000..6894d5d
--- /dev/null
+++ b/src/main/java/jnr/posix/SimpleFunctionMapper.java
@@ -0,0 +1,31 @@
+package jnr.posix;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public class SimpleFunctionMapper implements jnr.ffi.mapper.FunctionMapper {
+    private final Map<String, String> functionNameMap;
+    
+    private SimpleFunctionMapper(Map<String, String> map) {
+        functionNameMap = Collections.unmodifiableMap(new HashMap<String, String>(map));
+    }
+    
+    public String mapFunctionName(String functionName, Context context) {
+        String nativeFunction = functionNameMap.get(functionName);
+        return nativeFunction != null ? nativeFunction : functionName;
+    }
+    
+    public static class Builder {
+        private final Map<String, String> functionNameMap = Collections.synchronizedMap(new HashMap<String, String>());
+
+        public Builder map(String posixName, String nativeFunction) {
+            functionNameMap.put(posixName, nativeFunction);
+            return this;
+        }
+        
+        public SimpleFunctionMapper build() {
+            return new SimpleFunctionMapper(functionNameMap);        
+        }
+    } 
+}
diff --git a/src/main/java/jnr/posix/SocketMacros.java b/src/main/java/jnr/posix/SocketMacros.java
new file mode 100644
index 0000000..10de54a
--- /dev/null
+++ b/src/main/java/jnr/posix/SocketMacros.java
@@ -0,0 +1,12 @@
+package jnr.posix;
+
+import jnr.ffi.Pointer;
+
+/**
+ * @author Bob McWhirter
+ */
+public interface SocketMacros {
+    public int CMSG_SPACE(int l);
+    public int CMSG_LEN(int l);
+    public Pointer CMSG_DATA(Pointer cmsg);
+}
diff --git a/src/main/java/jnr/posix/SolarisFileStat32.java b/src/main/java/jnr/posix/SolarisFileStat32.java
new file mode 100644
index 0000000..ce22476
--- /dev/null
+++ b/src/main/java/jnr/posix/SolarisFileStat32.java
@@ -0,0 +1,107 @@
+
+package jnr.posix;
+
+import jnr.ffi.StructLayout;
+
+public class SolarisFileStat32 extends BaseFileStat implements NanosecondFileStat {
+    static final class Layout extends StructLayout {
+
+        Layout(jnr.ffi.Runtime runtime) {
+            super(runtime);
+        }
+        public static final int _ST_FSTYPSZ = 16;		/* array size for file system type name */
+
+        public final Signed32 st_dev = new Signed32();
+        public final SignedLong[] st_pad1 = array(new SignedLong[3]);
+        public final Signed64 st_ino = new Signed64();
+        public final Signed32 st_mode = new Signed32();
+        public final Signed32 st_nlink = new Signed32();
+        public final Signed32 st_uid = new Signed32();
+        public final Signed32 st_gid = new Signed32();
+        public final Signed32 st_rdev = new Signed32();
+        public final SignedLong[] st_pad2 = array(new SignedLong[2]);
+        public final Signed64 st_size = new Signed64();
+        public final Signed32 st_atim_sec = new Signed32();
+        public final Signed32 st_atim_nsec = new Signed32();
+        public final Signed32 st_mtim_sec = new Signed32();
+        public final Signed32 st_mtim_nsec = new Signed32();
+        public final Signed32 st_ctim_sec = new Signed32();
+        public final Signed32 st_ctim_nsec = new Signed32();
+        public final Signed32 st_blksize = new Signed32();
+        public final Signed64 st_blocks = new Signed64();
+        public final Signed8[] st_fstype = array(new Signed8[_ST_FSTYPSZ]);
+        public final SignedLong[] st_pad4 = array(new SignedLong[8]);
+    }
+    private static final Layout layout = new Layout(jnr.ffi.Runtime.getSystemRuntime());
+
+    public SolarisFileStat32(NativePOSIX posix) {
+        super(posix, layout);
+    }
+    
+    public long atime() {
+        return layout.st_atim_sec.get(memory);
+    }
+
+    public long blocks() {
+        return layout.st_blocks.get(memory);
+    }
+
+    public long blockSize() {
+        return layout.st_blksize.get(memory);
+    }
+
+    public long ctime() {
+        return layout.st_ctim_sec.get(memory);
+    }
+
+    public long dev() {
+        return layout.st_dev.get(memory);
+    }
+
+    public int gid() {
+        return layout.st_gid.get(memory);
+    }
+
+    public long ino() {
+        return layout.st_ino.get(memory);
+    }
+
+    public int mode() {
+        return layout.st_mode.get(memory) & 0xffff;
+    }
+
+    public long mtime() {
+        return layout.st_mtim_sec.get(memory);
+    }
+
+    public int nlink() {
+        return layout.st_nlink.get(memory);
+    }
+
+    public long rdev() {
+        return layout.st_rdev.get(memory);
+    }
+
+    public long st_size() {
+        return layout.st_size.get(memory);
+    }
+
+    public int uid() {
+        return layout.st_uid.get(memory);
+    }
+
+    @Override
+    public long aTimeNanoSecs() {
+        return layout.st_atim_nsec.get(memory);
+    }
+
+    @Override
+    public long cTimeNanoSecs() {
+        return layout.st_ctim_nsec.get(memory);
+    }
+
+    @Override
+    public long mTimeNanoSecs() {
+        return layout.st_mtim_nsec.get(memory);
+    }
+}
diff --git a/src/main/java/jnr/posix/SolarisFileStat64.java b/src/main/java/jnr/posix/SolarisFileStat64.java
new file mode 100644
index 0000000..a965451
--- /dev/null
+++ b/src/main/java/jnr/posix/SolarisFileStat64.java
@@ -0,0 +1,108 @@
+
+package jnr.posix;
+
+
+import jnr.ffi.*;
+
+public class SolarisFileStat64 extends BaseFileStat implements NanosecondFileStat {
+    static final class Layout extends StructLayout {
+
+        Layout(jnr.ffi.Runtime runtime) {
+            super(runtime);
+        }
+
+        public static final int _ST_FSTYPSZ = 16;		/* array size for file system type name */
+        public final UnsignedLong st_dev = new UnsignedLong();
+        public final Signed64 st_ino = new Signed64();
+        public final Signed32 st_mode = new Signed32();
+        public final Signed32 st_nlink = new Signed32();
+        public final Signed32 st_uid = new Signed32();
+        public final Signed32 st_gid = new Signed32();
+        public final UnsignedLong st_rdev = new UnsignedLong();
+        public final Signed64 st_size = new Signed64();
+        public final SignedLong st_atim_sec = new SignedLong();
+        public final SignedLong st_atim_nsec = new SignedLong();
+        public final SignedLong st_mtim_sec = new SignedLong();
+        public final SignedLong st_mtim_nsec = new SignedLong();
+        public final SignedLong st_ctim_sec = new SignedLong();
+        public final SignedLong st_ctim_nsec = new SignedLong();
+        public final Signed32 st_blksize = new Signed32();
+        public final Signed64 st_blocks = new Signed64();
+        public final Signed8[] st_fstype = array(new Signed8[_ST_FSTYPSZ]);
+    }
+    private static final Layout layout = new Layout(jnr.ffi.Runtime.getSystemRuntime());
+
+    public SolarisFileStat64() {
+        this(null);
+    }
+    public SolarisFileStat64(NativePOSIX posix) {
+        super(posix, layout);
+    }
+
+    public long atime() {
+        return layout.st_atim_sec.get(memory);
+    }
+
+    public long blocks() {
+        return layout.st_blocks.get(memory);
+    }
+
+    public long blockSize() {
+        return layout.st_blksize.get(memory);
+    }
+
+    public long ctime() {
+        return layout.st_ctim_sec.get(memory);
+    }
+
+    public long dev() {
+        return layout.st_dev.get(memory);
+    }
+
+    public int gid() {
+        return layout.st_gid.get(memory);
+    }
+
+    public long ino() {
+        return layout.st_ino.get(memory);
+    }
+
+    public int mode() {
+        return layout.st_mode.get(memory) & 0xffff;
+    }
+
+    public long mtime() {
+        return layout.st_mtim_sec.get(memory);
+    }
+
+    public int nlink() {
+        return layout.st_nlink.get(memory);
+    }
+
+    public long rdev() {
+        return layout.st_rdev.get(memory);
+    }
+
+    public long st_size() {
+        return layout.st_size.get(memory);
+    }
+
+    public int uid() {
+        return layout.st_uid.get(memory);
+    }
+
+    @Override
+    public long aTimeNanoSecs() {
+        return layout.st_atim_nsec.get(memory);
+    }
+
+    @Override
+    public long cTimeNanoSecs() {
+        return layout.st_ctim_nsec.get(memory);
+    }
+
+    @Override
+    public long mTimeNanoSecs() {
+        return layout.st_mtim_nsec.get(memory);
+    }
+}
diff --git a/src/main/java/jnr/posix/SolarisLibC.java b/src/main/java/jnr/posix/SolarisLibC.java
new file mode 100644
index 0000000..7efd010
--- /dev/null
+++ b/src/main/java/jnr/posix/SolarisLibC.java
@@ -0,0 +1,4 @@
+package jnr.posix;
+
+public interface SolarisLibC extends UnixLibC {
+}
diff --git a/src/main/java/jnr/posix/SolarisPOSIX.java b/src/main/java/jnr/posix/SolarisPOSIX.java
new file mode 100644
index 0000000..46c7367
--- /dev/null
+++ b/src/main/java/jnr/posix/SolarisPOSIX.java
@@ -0,0 +1,110 @@
+package jnr.posix;
+
+import java.nio.ByteBuffer;
+import jnr.constants.platform.Confstr;
+import jnr.constants.platform.Fcntl;
+import jnr.constants.platform.Sysconf;
+import jnr.ffi.*;
+import jnr.ffi.mapper.FromNativeContext;
+import jnr.posix.util.MethodName;
+import jnr.posix.util.Platform;
+
+import static jnr.constants.platform.Errno.EINVAL;
+import jnr.constants.platform.Pathconf;
+
+final class SolarisPOSIX extends BaseNativePOSIX {
+    SolarisPOSIX(LibCProvider libc, POSIXHandler handler) {
+        super(libc, handler);
+    }
+    
+    public FileStat allocateStat() {
+        return Platform.IS_32_BIT ? new SolarisFileStat32(this) : new SolarisFileStat64(this);
+    }
+
+    public MsgHdr allocateMsgHdr() {
+        handler.unimplementedError(MethodName.getCallerMethodName());
+        return null;
+    }
+
+    public SocketMacros socketMacros() {
+        handler.unimplementedError(MethodName.getCallerMethodName());
+        return null;
+    }
+
+    public long sysconf(Sysconf name) {
+        return libc().sysconf(name);
+    }
+
+    public int confstr(Confstr name, ByteBuffer buf, int len) {
+        return libc().confstr(name, buf, len);
+    }
+
+    public int fpathconf(int fd, Pathconf name) {
+        return libc().fpathconf(fd, name);
+    }
+
+    public Times times() {
+        return NativeTimes.times(this);
+    }
+
+    public static final int LOCK_SH = 1;
+    public static final int LOCK_EX = 2;
+    public static final int LOCK_NB = 4;
+    public static final int LOCK_UN = 8;
+
+    public static final int SEEK_SET = 0;
+
+    public static class Layout extends StructLayout {
+        protected Layout(jnr.ffi.Runtime runtime) {
+            super(runtime);
+        }
+
+        public final int16_t l_type = new int16_t(); // short
+        public final int16_t l_whence = new int16_t(); // short
+        public final off_t l_start = new off_t();
+        public final off_t l_len = new off_t();
+        public final int32_t l_sysid = new int32_t(); // int
+        public final pid_t l_pid = new pid_t();
+        public final int32_t[] l_pad = new int32_t[4]; // int[4]
+    }
+
+    private static final Layout FLOCK_LAYOUT = new Layout(jnr.ffi.Runtime.getSystemRuntime());
+
+    public int flock(int fd, int operation) {
+        Pointer lock = getRuntime().getMemoryManager().allocateTemporary(FLOCK_LAYOUT.size(), true);
+
+        switch (operation & ~LOCK_NB) {
+            case LOCK_SH:
+                FLOCK_LAYOUT.l_type.set(lock, (short) Fcntl.F_RDLCK.intValue());
+                break;
+            case LOCK_EX:
+                FLOCK_LAYOUT.l_type.set(lock, (short) Fcntl.F_WRLCK.intValue());
+                break;
+            case LOCK_UN:
+                FLOCK_LAYOUT.l_type.set(lock, (short) Fcntl.F_UNLCK.intValue());
+                break;
+            default:
+                errno(EINVAL.intValue());
+                return -1;
+        }
+        FLOCK_LAYOUT.l_whence.set(lock, (short) SEEK_SET);
+        FLOCK_LAYOUT.l_start.set(lock, 0);
+        FLOCK_LAYOUT.l_len.set(lock, 0);
+
+        return libc().fcntl(fd, (operation & LOCK_NB) != 0 ? Fcntl.F_SETLK.intValue() : Fcntl.F_SETLKW.intValue(), lock);
+    }
+
+    public static final PointerConverter PASSWD = new PointerConverter() {
+        public Object fromNative(Object arg, FromNativeContext ctx) {
+            return arg != null ? new SolarisPasswd((Pointer) arg) : null;
+        }
+    };
+
+    public Pointer allocatePosixSpawnFileActions() {
+        return Memory.allocateDirect(getRuntime(), 8);
+    }
+
+    public Pointer allocatePosixSpawnattr() {
+        return Memory.allocateDirect(getRuntime(), 8);
+    }
+}
diff --git a/src/main/java/jnr/posix/SolarisPasswd.java b/src/main/java/jnr/posix/SolarisPasswd.java
new file mode 100644
index 0000000..e2c8337
--- /dev/null
+++ b/src/main/java/jnr/posix/SolarisPasswd.java
@@ -0,0 +1,59 @@
+
+package jnr.posix;
+
+import jnr.ffi.StructLayout;
+
+public class SolarisPasswd extends NativePasswd implements Passwd {
+    static final class Layout extends StructLayout {
+
+        private Layout(jnr.ffi.Runtime runtime) {
+            super(runtime);
+        }
+
+        public final UTF8StringRef pw_name = new UTF8StringRef();   // user name
+        public final UTF8StringRef pw_passwd = new UTF8StringRef(); // password (encrypted)
+        public final Signed32 pw_uid = new Signed32();       // user id
+        public final Signed32 pw_gid = new Signed32();       // user id
+        public final Pointer pw_age = new Pointer();   // unused
+        public final Pointer pw_comment = new Pointer();// unused
+        public final UTF8StringRef pw_gecos = new UTF8StringRef();  // login info
+        public final UTF8StringRef pw_dir = new UTF8StringRef();    // home directory
+        public final UTF8StringRef pw_shell = new UTF8StringRef();  // default shell
+    }
+    private static final Layout layout = new Layout(jnr.ffi.Runtime.getSystemRuntime());
+
+    public SolarisPasswd(jnr.ffi.Pointer memory) {
+        super(memory);
+    }
+    
+    public java.lang.String getAccessClass() {
+        return "unknown";
+    }
+    public java.lang.String getGECOS() {
+        return layout.pw_gecos.get(memory);
+    }
+    public long getGID() {
+        return layout.pw_gid.get(memory);
+    }
+    public java.lang.String getHome() {
+        return layout.pw_dir.get(memory);
+    }
+    public java.lang.String getLoginName() {
+        return layout.pw_name.get(memory);
+    }
+    public int getPasswdChangeTime() {
+        return 0;
+    }
+    public java.lang.String getPassword() {
+        return layout.pw_passwd.get(memory);
+    }
+    public java.lang.String getShell() {
+        return layout.pw_shell.get(memory);
+    }
+    public long getUID() {
+        return layout.pw_uid.get(memory);
+    }
+    public int getExpire() {
+        return Integer.MAX_VALUE;
+    }
+}
diff --git a/src/main/java/jnr/posix/SpawnAttribute.java b/src/main/java/jnr/posix/SpawnAttribute.java
new file mode 100644
index 0000000..e9fb913
--- /dev/null
+++ b/src/main/java/jnr/posix/SpawnAttribute.java
@@ -0,0 +1,99 @@
+package jnr.posix;
+
+import jnr.ffi.*;
+import jnr.ffi.byref.NumberByReference;
+
+public abstract class SpawnAttribute {
+    public static final int RESETIDS   = 0x0001;  /* [SPN] R[UG]ID not E[UG]ID */
+    public static final int SETPGROUP  = 0x0002;  /* [SPN] set non-parent PGID */
+    public static final int SETSIGDEF  = 0x0004;  /* [SPN] reset sigset default */
+    public static final int SETSIGMASK = 0x0008;  /* [SPN] set signal mask */
+    
+    abstract boolean set(POSIX posix, Pointer nativeFileActions);
+
+    public static SpawnAttribute pgroup(long pgroup) {
+        return new PGroup(pgroup);
+    }
+
+    public static SpawnAttribute flags(short flags) {
+        return new SetFlags(flags);
+    }
+
+    public static SpawnAttribute sigdef(long sigdef) {
+        throw new RuntimeException("sigdefault not yet supported");
+//        return new Sigdef(sigdef);
+    }
+
+    public static SpawnAttribute sigmask(long sigmask) {
+        throw new RuntimeException("sigmask not yet supported");
+//        return new Sigmask(sigmask);
+    }
+
+
+    private static final class PGroup extends SpawnAttribute {
+        final long pgroup;
+
+        public PGroup(long pgroup) {
+            this.pgroup = pgroup;
+        }
+
+        final boolean set(POSIX posix, Pointer nativeSpawnAttr) {
+            return ((UnixLibC) posix.libc()).posix_spawnattr_setpgroup(nativeSpawnAttr, pgroup) == 0;
+        }
+
+        public String toString() {
+            return "SpawnAttribute::PGroup(pgroup = " + pgroup + ")";
+        }
+    }
+
+    private static final class SetFlags extends SpawnAttribute {
+        final short flags;
+
+        public SetFlags(short flags) {
+            this.flags = flags;
+        }
+
+        final boolean set(POSIX posix, Pointer nativeSpawnAttr) {
+            return ((UnixLibC) posix.libc()).posix_spawnattr_setflags(nativeSpawnAttr, flags) == 0;
+        }
+
+        public String toString() {
+            return "SpawnAttribute::SetFlags(flags = " + Integer.toHexString(flags) + ")";
+        }
+    }
+
+    private static final class Sigmask extends SpawnAttribute {
+        final long sigmask;
+
+        public Sigmask(long sigmask) {
+            this.sigmask = sigmask;
+        }
+
+        final boolean set(POSIX posix, Pointer nativeSpawnAttr) {
+            throw new RuntimeException("sigmask not yet supported");
+//            return ((UnixLibC) posix.libc()).posix_spawnattr_setsigmask(nativeSpawnAttr, mask) == 0;
+        }
+
+        public String toString() {
+            return "SpawnAttribute::Sigmask(mask = " + Long.toHexString(sigmask) + ")";
+        }
+    }
+
+    private static final class Sigdef extends SpawnAttribute {
+        final long sigdef;
+
+        public Sigdef(long sigdef) {
+            this.sigdef = sigdef;
+        }
+
+        final boolean set(POSIX posix, Pointer nativeSpawnAttr) {
+            throw new RuntimeException("sigdefault not yet supported");
+//            return ((UnixLibC) posix.libc()).posix_spawnattr_setsigdefault(nativeSpawnAttr, sigdef) == 0;
+        }
+
+        public String toString() {
+            return "SpawnAttribute::Sigdef(def = " + Long.toHexString(sigdef) + ")";
+        }
+    }
+
+}
diff --git a/src/main/java/jnr/posix/SpawnFileAction.java b/src/main/java/jnr/posix/SpawnFileAction.java
new file mode 100644
index 0000000..1657b3f
--- /dev/null
+++ b/src/main/java/jnr/posix/SpawnFileAction.java
@@ -0,0 +1,106 @@
+package jnr.posix;
+
+import jnr.ffi.Pointer;
+
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+
+public abstract class SpawnFileAction {
+    abstract boolean act(POSIX posix, Pointer nativeFileActions);
+
+    public static SpawnFileAction dup(int fd, int newfd) {
+        return new Dup(fd, newfd);
+    }
+
+    public static SpawnFileAction open(String path, int fd, int flags, int mode) {
+        return new Open(path, fd, flags, mode);
+    }
+
+    public static SpawnFileAction close(int fd) {
+        return new Close(fd);
+    }
+
+    private static final class Dup extends SpawnFileAction {
+        final int fd, newfd;
+
+        public Dup(int fd, int newfd) {
+            this.fd = fd;
+            this.newfd = newfd;
+        }
+
+        final boolean act(POSIX posix, Pointer nativeFileActions) {
+            return ((UnixLibC) posix.libc()).posix_spawn_file_actions_adddup2(nativeFileActions, fd, newfd) == 0;
+        }
+
+        public String toString() {
+            return "SpawnFileAction::Dup(old = " + fd + ", new = " + newfd + ")";
+        }
+    }
+
+    private static final class Open extends SpawnFileAction {
+        final String path;
+        final int fd;
+        final int flags, mode;
+        final ByteBuffer nativePath;
+
+        public Open(String path, int fd, int flags, int mode) {
+            this.path = path;
+            this.fd = fd;
+            this.flags = flags;
+            this.mode = mode;
+            this.nativePath = defensiveCopy(path);
+        }
+
+        private ByteBuffer defensiveCopy(String path) {
+            /*
+            This logic allocates a direct ByteBuffer to use for the path in order to work around systems that have not
+            patched CVE-2014-4043, in which older glibc versions do not make a defensive copy of the file path passed to
+            posix_spawn_file_actions_addopen. The buffer may be freed by the caller before it can be used in an
+            eventual posix_spawn call.
+
+            See https://bugzilla.redhat.com/show_bug.cgi?id=1983750 for a RHEL version of this issue.
+            */
+
+            // determine encoded byte array length
+            CharsetEncoder encoder = Charset.defaultCharset().newEncoder();
+            int bpc = (int) encoder.maxBytesPerChar();
+            int size = (path.length() + 1) * bpc;
+
+            // transcode to native buffer
+            ByteBuffer nativePath = ByteBuffer.allocateDirect(size);
+            encoder.encode(CharBuffer.wrap(path), nativePath, true);
+            nativePath.flip();
+
+            // null terminate
+            nativePath.limit(nativePath.limit() + bpc);
+
+            return nativePath;
+        }
+
+        final boolean act(POSIX posix, Pointer nativeFileActions) {
+            return ((UnixLibC) posix.libc()).posix_spawn_file_actions_addopen(nativeFileActions, fd, nativePath, flags, mode) == 0;
+        }
+
+        public String toString() {
+            return "SpawnFileAction::Open(path = '" + path + "', fd = " + fd + ", flags = " + Integer.toHexString(flags) + ", mode = " + Integer.toHexString(mode) + ")";
+        }
+    }
+
+    private static final class Close extends SpawnFileAction {
+        final int fd;
+
+        public Close(int fd) {
+            this.fd = fd;
+        }
+
+        final boolean act(POSIX posix, Pointer nativeFileActions) {
+            return ((UnixLibC) posix.libc()).posix_spawn_file_actions_addclose(nativeFileActions, fd) == 0;
+        }
+
+        public String toString() {
+            return "SpawnFileAction::Close(fd = " + fd + ")";
+        }
+    }
+}
diff --git a/src/main/java/jnr/posix/Times.java b/src/main/java/jnr/posix/Times.java
new file mode 100644
index 0000000..ed34fce
--- /dev/null
+++ b/src/main/java/jnr/posix/Times.java
@@ -0,0 +1,9 @@
+package jnr.posix;
+
+
+public interface Times {
+    public abstract long utime();
+    public abstract long stime();
+    public abstract long cutime();
+    public abstract long cstime();
+}
diff --git a/src/main/java/jnr/posix/Timespec.java b/src/main/java/jnr/posix/Timespec.java
new file mode 100644
index 0000000..a092070
--- /dev/null
+++ b/src/main/java/jnr/posix/Timespec.java
@@ -0,0 +1,14 @@
+package jnr.posix;
+
+import jnr.ffi.Struct;
+
+abstract public class Timespec extends Struct {
+    public Timespec(jnr.ffi.Runtime runtime) {
+        super(runtime);
+    }
+    abstract public void setTime(long[] timespec);
+    public abstract void sec(long sec);
+    public abstract void nsec(long nsec);
+    public abstract long sec();
+    public abstract long nsec();
+}
diff --git a/src/main/java/jnr/posix/Timeval.java b/src/main/java/jnr/posix/Timeval.java
new file mode 100644
index 0000000..e5942e4
--- /dev/null
+++ b/src/main/java/jnr/posix/Timeval.java
@@ -0,0 +1,14 @@
+package jnr.posix;
+
+import jnr.ffi.Struct;
+
+abstract public class Timeval extends Struct {
+    public Timeval(jnr.ffi.Runtime runtime) {
+        super(runtime);
+    }
+    abstract public void setTime(long[] timeval);
+    public abstract void sec(long sec);
+    public abstract void usec(long usec);
+    public abstract long sec();
+    public abstract long usec();
+}
diff --git a/src/main/java/jnr/posix/UTimBuf64.java b/src/main/java/jnr/posix/UTimBuf64.java
new file mode 100644
index 0000000..f836e4e
--- /dev/null
+++ b/src/main/java/jnr/posix/UTimBuf64.java
@@ -0,0 +1,14 @@
+package jnr.posix;
+
+import jnr.ffi.Struct;
+
+public final class UTimBuf64 extends Struct {
+    public final Signed64 actime = new Signed64();
+    public final Signed64 modtime = new Signed64();
+
+    public UTimBuf64(jnr.ffi.Runtime runtime, long actime, long modtime) {
+        super(runtime);
+        this.actime.set(actime);
+        this.modtime.set(modtime);
+    }
+}
diff --git a/src/main/java/jnr/posix/UnixLibC.java b/src/main/java/jnr/posix/UnixLibC.java
new file mode 100644
index 0000000..89666be
--- /dev/null
+++ b/src/main/java/jnr/posix/UnixLibC.java
@@ -0,0 +1,48 @@
+package jnr.posix;
+
+import jnr.ffi.Pointer;
+import jnr.ffi.annotations.Direct;
+import jnr.ffi.annotations.In;
+import jnr.ffi.annotations.Out;
+import jnr.ffi.byref.ByReference;
+import jnr.ffi.byref.IntByReference;
+import jnr.ffi.byref.NumberByReference;
+import jnr.ffi.byref.ShortByReference;
+import jnr.ffi.types.pid_t;
+
+import java.nio.ByteBuffer;
+
+public interface UnixLibC extends LibC {
+    public int posix_spawn(@Out ByReference pid, @In CharSequence path, @In Pointer fileActions,
+                           @In Pointer attr, @In CharSequence[] argv, @In CharSequence[] envp);
+
+    public int posix_spawnp(@Out ByReference pid, @In CharSequence path, @In Pointer fileActions,
+                            @In Pointer attr, @In CharSequence[] argv, @In CharSequence[] envp);
+
+    public int posix_spawn_file_actions_init(Pointer fileActions);
+    public int posix_spawn_file_actions_destroy(Pointer fileActions);
+    public int posix_spawn_file_actions_addclose(Pointer fileActions, int filedes);
+
+    /**
+     * @deprecated due to CVE-2014-4043 (https://bugzilla.redhat.com/show_bug.cgi?id=1983750)
+     */
+    @Deprecated
+    public int posix_spawn_file_actions_addopen(Pointer fileActions, int filedes, CharSequence path,
+                                                int oflag, int mode);
+    public int posix_spawn_file_actions_addopen(Pointer fileActions, int filedes, @Direct ByteBuffer path,
+                                                int oflag, int mode);
+    public int posix_spawn_file_actions_adddup2(Pointer fileActions, int filedes, int newfiledes);
+    public int posix_spawnattr_init(Pointer attr);
+    public int posix_spawnattr_destroy(Pointer attr);
+    public int posix_spawnattr_setflags(Pointer attr, short flags);
+    public int posix_spawnattr_getflags(Pointer attr, ShortByReference flags);
+    public int posix_spawnattr_setpgroup(Pointer attr, @pid_t long pgroup);
+    public int posix_spawnattr_getpgroup(Pointer attr, NumberByReference pgroup);
+    public int posix_spawnattr_setsigmask(Pointer attr, Pointer sigmask);
+    public int posix_spawnattr_getsigmask(Pointer attr, Pointer sigmask);
+    public int posix_spawnattr_setsigdefault(Pointer attr, Pointer sigdefault);
+    public int posix_spawnattr_getsigdefault(Pointer attr, Pointer sigdefault);
+    public int sigprocmask(int how, Pointer set, Pointer get);
+
+    int mkfifo(CharSequence filename, int mode);
+}
diff --git a/src/main/java/jnr/posix/WString.java b/src/main/java/jnr/posix/WString.java
new file mode 100644
index 0000000..590a858
--- /dev/null
+++ b/src/main/java/jnr/posix/WString.java
@@ -0,0 +1,60 @@
+package jnr.posix;
+
+import jnr.ffi.Memory;
+import jnr.ffi.Pointer;
+import jnr.ffi.mapper.ToNativeContext;
+import jnr.ffi.mapper.ToNativeConverter;
+import jnr.posix.util.WindowsHelpers;
+
+public final class WString {
+    static final jnr.ffi.Runtime runtime = jnr.ffi.Runtime.getSystemRuntime();
+
+    private final byte[] bytes;
+
+    WString(String string) {
+        bytes = WindowsHelpers.toWString(string);
+    }
+
+    private WString(byte[] bytes) {
+        this.bytes = bytes;
+    }
+
+    public static WString path(String path) {
+        return new WString(path(path, false));
+    }
+
+    public static byte[] path(String path, boolean longPathExtensionNeeded) {
+        if (longPathExtensionNeeded && path.length() > 240) { // FIXME: This is not right value.  Needs tests around actual char boundary.
+            if (path.startsWith("//")) { // UNC Path
+                path = "//?/UNC/" + path.substring(2);
+            } else if (path.startsWith("\\\\")) {
+                path = "\\\\?\\UNC\\" + path.substring(2);
+            } else if (WindowsHelpers.isDriveLetterPath(path)) {
+                if (path.contains("/")) {
+                    path = "//?/" + path;
+                } else {
+                    path = "\\\\?\\" + path;
+                }
+            }
+        }
+
+        return WindowsHelpers.toWPath(path);
+    }
+
+    public static final ToNativeConverter<WString, Pointer> Converter = new ToNativeConverter<WString, Pointer>() {
+
+        public Pointer toNative(WString value, ToNativeContext context) {
+            if (value == null) {
+                return null;
+            }
+
+            Pointer memory = Memory.allocateDirect(runtime, value.bytes.length + 1, true);
+            memory.put(0, value.bytes, 0, value.bytes.length);
+            return memory;
+        }
+
+        public Class<Pointer> nativeType() {
+            return Pointer.class;
+        }
+    };
+}
diff --git a/src/main/java/jnr/posix/WindowsChildRecord.java b/src/main/java/jnr/posix/WindowsChildRecord.java
new file mode 100644
index 0000000..d4442e6
--- /dev/null
+++ b/src/main/java/jnr/posix/WindowsChildRecord.java
@@ -0,0 +1,28 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package jnr.posix;
+
+/**
+ *
+ * @author enebo
+ */
+public class WindowsChildRecord {
+    private final HANDLE process;
+    private final int pid;
+
+    public WindowsChildRecord(HANDLE process, int pid) {
+        this.process = process;
+        this.pid = pid;
+    }
+    
+    public HANDLE getProcess() {
+        return process;
+    }
+    
+    public int getPid() {
+        return pid;
+    }
+}
diff --git a/src/main/java/jnr/posix/WindowsFileStat.java b/src/main/java/jnr/posix/WindowsFileStat.java
new file mode 100644
index 0000000..96120e7
--- /dev/null
+++ b/src/main/java/jnr/posix/WindowsFileStat.java
@@ -0,0 +1,169 @@
+package jnr.posix;
+
+import jnr.ffi.StructLayout;
+
+// http://msdn.microsoft.com/en-us/library/14h5k7ff.aspx
+// This layout is meant to be used with stat64() family so _USE_32BIT_TIME_T is not in play.
+public class WindowsFileStat extends BaseFileStat {
+    private static final class Layout extends StructLayout {
+
+        private Layout(jnr.ffi.Runtime runtime) {
+            super(runtime);
+        }
+
+        public final Signed32 st_dev = new Signed32();
+        public final Signed16 st_ino = new Signed16();
+        public final Signed16 st_mode = new Signed16();
+        public final Signed16 st_nlink = new Signed16();
+        public final Signed16 st_uid = new Signed16();
+        public final Signed16 st_gid = new Signed16();
+        public final Signed32 st_rdev = new Signed32();
+        public final Signed64 st_size = new Signed64();
+        public final Signed64 st_atime = new Signed64();
+        public final Signed64 st_mtime = new Signed64();
+        public final Signed64 st_ctime = new Signed64();
+    }
+    private static final Layout layout = new Layout(jnr.ffi.Runtime.getSystemRuntime());
+
+    public WindowsFileStat(NativePOSIX posix) {
+        super(posix, layout);
+    }
+
+    public long atime() {
+        return layout.st_atime.get(memory);
+    }
+
+    public long blockSize() {
+        return 512;
+    }
+
+    public long blocks() {
+        return (layout.st_size.get(memory) + 512 - 1) / 512;
+    }
+
+    public long ctime() {
+        return layout.st_ctime.get(memory);
+    }
+
+    public long dev() {
+        return layout.st_dev.get(memory);
+    }
+
+    public int gid() {
+        return layout.st_gid.get(memory);
+    }
+
+    public long ino() {
+        return layout.st_ino.get(memory);
+    }
+
+    public int mode() {
+        return layout.st_mode.get(memory) & ~(S_IWGRP | S_IWOTH) & 0xffff;
+    }
+
+    public long mtime() {
+        return layout.st_mtime.get(memory);
+    }
+
+    public int nlink() {
+        return layout.st_nlink.get(memory);
+    }
+
+    public long rdev() {
+        return layout.st_rdev.get(memory);
+    }
+
+    public long st_size() {
+        return layout.st_size.get(memory);
+    }
+
+    public int uid() {
+        return layout.st_uid.get(memory);
+    }
+
+    // FIXME: Implement
+    @Override
+    public boolean groupMember(int gid) {
+        return true;
+    }
+
+    @Override
+    public boolean isExecutable() {
+        if (isOwned()) return (mode() & S_IXUSR) != 0;
+        if (isGroupOwned()) return (mode() & S_IXGRP) != 0;
+        if ((mode() & S_IXOTH) != 0) return false;
+
+        return true;
+    }
+
+    @Override
+    public boolean isExecutableReal() {
+        if (isROwned()) return (mode() & S_IXUSR) != 0;
+        if (groupMember(gid())) return (mode() & S_IXGRP) != 0;
+        if ((mode() & S_IXOTH) != 0) return false;
+
+        return true;
+    }
+
+    // FIXME: Implement
+    @Override
+    public boolean isOwned() {
+        return true;
+    }
+
+    // FIXME: Implement
+    @Override
+    public boolean isROwned() {
+        return true;
+    }
+    @Override
+    public boolean isReadable() {
+        if (isOwned()) return (mode() & S_IRUSR) != 0;
+        if (isGroupOwned()) return (mode() & S_IRGRP) != 0;
+        if ((mode() & S_IROTH) != 0) return false;
+
+        return true;
+    }
+
+    @Override
+    public boolean isReadableReal() {
+        if (isROwned()) return (mode() & S_IRUSR) != 0;
+        if (groupMember(gid())) return (mode() & S_IRGRP) != 0;
+        if ((mode() & S_IROTH) != 0) return false;
+
+        return true;
+    }
+
+    @Override
+    public boolean isWritable() {
+        if (isOwned()) return (mode() & S_IWUSR) != 0;
+        if (isGroupOwned()) return (mode() & S_IWGRP) != 0;
+        if ((mode() & S_IWOTH) != 0) return false;
+
+        return true;
+    }
+
+    @Override
+    public boolean isWritableReal() {
+        if (isROwned()) return (mode() & S_IWUSR) != 0;
+        if (groupMember(gid())) return (mode() & S_IWGRP) != 0;
+        if ((mode() & S_IWOTH) != 0) return false;
+
+        return true;
+    }
+
+    @Override
+    public java.lang.String toString() {
+        return "st_dev: " + layout.st_dev.get(memory) +
+                ", st_mode: " + Integer.toOctalString(mode()) +
+                ", layout.st_nlink: " + layout.st_nlink.get(memory) +
+                ", layout.st_rdev: " + layout.st_rdev.get(memory) +
+                ", layout.st_size: " + layout.st_size.get(memory) +
+                ", layout.st_uid: " + layout.st_uid.get(memory) +
+                ", layout.st_gid: " + layout.st_gid.get(memory) +
+                ", layout.st_atime: " + layout.st_atime.get(memory) +
+                ", layout.st_ctime: " + layout.st_ctime.get(memory) +
+                ", layout.st_mtime: " + layout.st_mtime.get(memory) +
+                ", layout.st_ino: " + layout.st_ino.get(memory);
+    }
+}
diff --git a/src/main/java/jnr/posix/WindowsLibC.java b/src/main/java/jnr/posix/WindowsLibC.java
new file mode 100644
index 0000000..495f399
--- /dev/null
+++ b/src/main/java/jnr/posix/WindowsLibC.java
@@ -0,0 +1,130 @@
+package jnr.posix;
+
+import jnr.ffi.Pointer;
+import jnr.ffi.Variable;
+import jnr.ffi.annotations.In;
+import jnr.ffi.annotations.Out;
+import jnr.ffi.annotations.StdCall;
+import jnr.ffi.annotations.Transient;
+import jnr.ffi.byref.IntByReference;
+
+import java.nio.ByteBuffer;
+import jnr.posix.windows.SystemTime;
+import jnr.posix.windows.WindowsByHandleFileInformation;
+import jnr.posix.windows.WindowsFileInformation;
+import jnr.posix.windows.WindowsFindData;
+
+public interface WindowsLibC extends LibC {
+    public static final int STD_INPUT_HANDLE = -10;
+    public static final int STD_OUTPUT_HANDLE = -11;
+    public static final int STD_ERROR_HANDLE = -12;
+
+    public static final int NORMAL_PRIORITY_CLASS = 0x00000020;
+    public static final int CREATE_UNICODE_ENVIRONMENT = 0x00000400;
+    
+    public static final int INFINITE = -1;
+
+    public static final int FILE_TYPE_DISK = 0x0001;
+    public static final int FILE_TYPE_CHAR = 0x0002;
+    public static final int FILE_TYPE_PIPE = 0x0003;
+    public static final int FILE_TYPE_REMOTE = 0x8000;
+    public static final int FILE_TYPE_UNKNOWN = 0x0000;
+
+    public static final int PROCESS_QUERY_INFORMATION = 0x0400;
+
+    public int _open_osfhandle(HANDLE handle, int flags);
+    public HANDLE _get_osfhandle(int fd);
+    public int _close(int fd);
+    public int _getpid();
+    int _stat64(CharSequence path, @Out @Transient FileStat stat);
+
+    int _umask(int mask);
+
+    public int _wmkdir(@In WString path);
+    public boolean RemoveDirectoryW(@In WString path);
+    public int _wchmod(@In WString path, int pmode);
+    public int _wchdir(@In WString path);
+    public int _wstat64(@In WString path, @Out @Transient FileStat stat);
+    public int _wstat64(@In byte[] path, @Out @Transient FileStat stat);
+    public int _pipe(int[] fds, int psize, int textmode);
+    
+    @StdCall
+    public boolean CreateProcessW(byte[] applicationName, 
+                                 @In @Out ByteBuffer buffer, 
+                                 WindowsSecurityAttributes processAttributes,
+                                 WindowsSecurityAttributes threadAttributes,
+                                 int inheritHandles,
+                                 int creationFlags,
+                                 @In Pointer envp,
+                                 @In byte[] currentDirectory,
+                                 WindowsStartupInfo startupInfo,
+                                 WindowsProcessInformation processInformation);
+
+    public HANDLE OpenProcess(@In int desiredAccess, @In int inheritHandle, @In int processId);
+
+    public int FileTimeToSystemTime(@In FileTime fileTime, @Out @Transient SystemTime systemTime);
+    public int GetFileAttributesW(@In WString path);
+    public int GetFileAttributesExW(@In WString path, @In int infoLevel, @Out @Transient WindowsFileInformation fileInformation);
+    public int GetFileAttributesExW(@In byte[] path, @In int infoLevel, @Out @Transient WindowsFileInformation fileInformation);
+    public int SetFileAttributesW(@In WString path, int flags);
+    public int GetFileInformationByHandle(@In HANDLE handle, @Out @Transient WindowsByHandleFileInformation fileInformation);
+
+    public int FindClose(HANDLE handle);
+    public HANDLE FindFirstFileW(@In WString wpath, @Out WindowsFindData findData);
+    public HANDLE FindFirstFileW(@In byte[] wpath, @Out WindowsFindData findData);
+    
+    @StdCall
+    public boolean GetExitCodeProcess(HANDLE handle, @Out Pointer exitCode);
+
+    @StdCall
+    public boolean GetExitCodeProcess(HANDLE handle, @Out IntByReference exitCode);
+
+    @StdCall
+    public int GetFileType(HANDLE handle);
+
+    @StdCall
+    public int GetFileSize(HANDLE handle, @Out IntByReference outSizeHigh);
+    
+    @StdCall
+    public HANDLE GetStdHandle(int stdHandle);
+
+    @StdCall
+    public boolean CreateHardLinkW(@In WString oldname, @In WString newName, @In WString reserved);
+
+    @StdCall
+    HANDLE CreateFileW(
+            byte[] lpFileName,
+            int dwDesiredAccess,
+            int dwShareMode,
+            Pointer lpSecurityAttributes,
+            int dwCreationDisposition,
+            int dwFlagsAndAttributes,
+            int hTemplateFile
+    );
+    
+    @StdCall
+    boolean SetEnvironmentVariableW(
+            @In WString envName,
+            @In WString envValue);
+
+    @StdCall
+    boolean GetComputerNameW(
+            @Out ByteBuffer lpBuffer,
+            IntByReference nSize);
+
+    @StdCall
+    boolean SetFileTime(
+            HANDLE  hFile,
+            FileTime lpCreationTime,
+            FileTime lpLastAccessTime,
+            FileTime lpLastWriteTime
+    );
+
+    @StdCall
+    boolean CloseHandle(HANDLE handle);
+    
+    @StdCall
+    int WaitForSingleObject(HANDLE handle, int milliseconds);
+
+    Variable<Long> _environ();
+}
diff --git a/src/main/java/jnr/posix/WindowsPOSIX.java b/src/main/java/jnr/posix/WindowsPOSIX.java
new file mode 100644
index 0000000..926a683
--- /dev/null
+++ b/src/main/java/jnr/posix/WindowsPOSIX.java
@@ -0,0 +1,921 @@
+package jnr.posix;
+
+import jnr.constants.platform.OpenFlags;
+import jnr.constants.platform.Fcntl;
+import jnr.constants.platform.Errno;
+import jnr.constants.platform.WaitFlags;
+import static jnr.constants.platform.Errno.*;
+import static jnr.constants.platform.windows.LastError.*;
+
+import jnr.ffi.LastError;
+import jnr.ffi.Pointer;
+import jnr.ffi.byref.IntByReference;
+import jnr.ffi.mapper.FromNativeContext;
+
+import java.io.FileDescriptor;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.HashMap;
+import java.util.Map;
+
+import jnr.posix.util.MethodName;
+import jnr.posix.util.Platform;
+import jnr.posix.util.WindowsHelpers;
+import jnr.posix.windows.CommonFileInformation;
+import jnr.posix.windows.WindowsByHandleFileInformation;
+import jnr.posix.windows.WindowsFileInformation;
+import jnr.posix.windows.WindowsFindData;
+
+final public class WindowsPOSIX extends BaseNativePOSIX {
+    private final static int FILE_TYPE_CHAR = 0x0002;
+
+    private final static Map<Integer, Errno> errorToErrnoMapper
+            = new HashMap<Integer, Errno>();
+
+    static {
+        errorToErrnoMapper.put(ERROR_INVALID_FUNCTION.value(), EINVAL);
+        errorToErrnoMapper.put(ERROR_FILE_NOT_FOUND.value(), ENOENT);
+        errorToErrnoMapper.put(ERROR_PATH_NOT_FOUND.value(), ENOENT);
+        errorToErrnoMapper.put(ERROR_TOO_MANY_OPEN_FILES.value(), EMFILE);
+        errorToErrnoMapper.put(ERROR_ACCESS_DENIED.value(), EACCES);
+        errorToErrnoMapper.put(ERROR_INVALID_HANDLE.value(), EBADF );
+        errorToErrnoMapper.put(ERROR_ARENA_TRASHED.value(), ENOMEM);
+        errorToErrnoMapper.put(ERROR_NOT_ENOUGH_MEMORY.value(), ENOMEM);
+        errorToErrnoMapper.put(ERROR_INVALID_BLOCK.value(), ENOMEM);
+        errorToErrnoMapper.put(ERROR_BAD_ENVIRONMENT.value(), E2BIG );
+        errorToErrnoMapper.put(ERROR_BAD_FORMAT.value(), ENOEXEC);
+        errorToErrnoMapper.put(ERROR_INVALID_ACCESS.value(), EINVAL);
+        errorToErrnoMapper.put(ERROR_INVALID_DATA.value(), EINVAL);
+        errorToErrnoMapper.put(ERROR_INVALID_DRIVE.value(), ENOENT);
+        errorToErrnoMapper.put(ERROR_CURRENT_DIRECTORY.value(), EACCES);
+        errorToErrnoMapper.put(ERROR_NOT_SAME_DEVICE.value(), EXDEV );
+        errorToErrnoMapper.put(ERROR_NO_MORE_FILES.value(), ENOENT);
+        errorToErrnoMapper.put(ERROR_WRITE_PROTECT.value(), EROFS );
+        errorToErrnoMapper.put(ERROR_BAD_UNIT.value(), ENODEV);
+        errorToErrnoMapper.put(ERROR_NOT_READY.value(), ENXIO );
+        errorToErrnoMapper.put(ERROR_BAD_COMMAND.value(), EACCES);
+        errorToErrnoMapper.put(ERROR_CRC.value(), EACCES);
+        errorToErrnoMapper.put(ERROR_BAD_LENGTH.value(), EACCES);
+        errorToErrnoMapper.put(ERROR_SEEK.value(), EIO);
+        errorToErrnoMapper.put(ERROR_NOT_DOS_DISK.value(), EACCES);
+        errorToErrnoMapper.put(ERROR_SECTOR_NOT_FOUND.value(), EACCES);
+        errorToErrnoMapper.put(ERROR_OUT_OF_PAPER.value(), EACCES);
+        errorToErrnoMapper.put(ERROR_WRITE_FAULT.value(), EIO);
+        errorToErrnoMapper.put(ERROR_READ_FAULT.value(), EIO);
+        errorToErrnoMapper.put(ERROR_GEN_FAILURE.value(), EACCES);
+        errorToErrnoMapper.put(ERROR_LOCK_VIOLATION.value(), EACCES);
+        errorToErrnoMapper.put(ERROR_SHARING_VIOLATION.value(), EACCES);
+        errorToErrnoMapper.put(ERROR_WRONG_DISK.value(), EACCES);
+        errorToErrnoMapper.put(ERROR_SHARING_BUFFER_EXCEEDED.value(), EACCES);
+        errorToErrnoMapper.put(ERROR_BAD_NETPATH.value(), ENOENT);
+        errorToErrnoMapper.put(ERROR_NETWORK_ACCESS_DENIED.value(), EACCES);
+        errorToErrnoMapper.put(ERROR_BAD_NET_NAME.value(), ENOENT);
+        errorToErrnoMapper.put(ERROR_FILE_EXISTS.value(), EEXIST);
+        errorToErrnoMapper.put(ERROR_CANNOT_MAKE.value(), EACCES);
+        errorToErrnoMapper.put(ERROR_FAIL_I24.value(), EACCES);
+        errorToErrnoMapper.put(ERROR_INVALID_PARAMETER.value(), EINVAL);
+        errorToErrnoMapper.put(ERROR_NO_PROC_SLOTS.value(), EAGAIN);
+        errorToErrnoMapper.put(ERROR_DRIVE_LOCKED.value(), EACCES);
+        errorToErrnoMapper.put(ERROR_BROKEN_PIPE.value(), EPIPE);
+        errorToErrnoMapper.put(ERROR_DISK_FULL.value(), ENOSPC);
+        errorToErrnoMapper.put(ERROR_INVALID_TARGET_HANDLE.value(), EBADF);
+        errorToErrnoMapper.put(ERROR_INVALID_HANDLE.value(), EINVAL);
+        errorToErrnoMapper.put(ERROR_WAIT_NO_CHILDREN.value(), ECHILD);
+        errorToErrnoMapper.put(ERROR_CHILD_NOT_COMPLETE.value(), ECHILD);
+        errorToErrnoMapper.put(ERROR_DIRECT_ACCESS_HANDLE.value(), EBADF);
+        errorToErrnoMapper.put(ERROR_NEGATIVE_SEEK.value(), EINVAL);
+        errorToErrnoMapper.put(ERROR_SEEK_ON_DEVICE.value(), EACCES);
+        errorToErrnoMapper.put(ERROR_DIR_NOT_EMPTY.value(), ENOTEMPTY);
+        errorToErrnoMapper.put(ERROR_DIRECTORY.value(), ENOTDIR);
+        errorToErrnoMapper.put(ERROR_NOT_LOCKED.value(), EACCES);
+        errorToErrnoMapper.put(ERROR_BAD_PATHNAME.value(), ENOENT);
+        errorToErrnoMapper.put(ERROR_MAX_THRDS_REACHED.value(), EAGAIN);
+        errorToErrnoMapper.put(ERROR_LOCK_FAILED.value(), EACCES);
+        errorToErrnoMapper.put(ERROR_ALREADY_EXISTS.value(), EEXIST);
+        errorToErrnoMapper.put(ERROR_INVALID_STARTING_CODESEG.value(), ENOEXEC);
+        errorToErrnoMapper.put(ERROR_INVALID_STACKSEG.value(), ENOEXEC);
+        errorToErrnoMapper.put(ERROR_INVALID_MODULETYPE.value(), ENOEXEC);
+        errorToErrnoMapper.put(ERROR_INVALID_EXE_SIGNATURE.value(), ENOEXEC);
+        errorToErrnoMapper.put(ERROR_EXE_MARKED_INVALID.value(), ENOEXEC);
+        errorToErrnoMapper.put(ERROR_BAD_EXE_FORMAT.value(), ENOEXEC);
+        errorToErrnoMapper.put(ERROR_ITERATED_DATA_EXCEEDS_64k.value(), ENOEXEC);
+        errorToErrnoMapper.put(ERROR_INVALID_MINALLOCSIZE.value(), ENOEXEC);
+        errorToErrnoMapper.put(ERROR_DYNLINK_FROM_INVALID_RING.value(),ENOEXEC);
+        errorToErrnoMapper.put(ERROR_IOPL_NOT_ENABLED.value(), ENOEXEC);
+        errorToErrnoMapper.put(ERROR_INVALID_SEGDPL.value(), ENOEXEC);
+        errorToErrnoMapper.put(ERROR_AUTODATASEG_EXCEEDS_64k.value(), ENOEXEC);
+        errorToErrnoMapper.put(ERROR_RING2SEG_MUST_BE_MOVABLE.value(), ENOEXEC);
+        errorToErrnoMapper.put(ERROR_RELOC_CHAIN_XEEDS_SEGLIM.value(), ENOEXEC);
+        errorToErrnoMapper.put(ERROR_INFLOOP_IN_RELOC_CHAIN.value(), ENOEXEC);
+        errorToErrnoMapper.put(ERROR_FILENAME_EXCED_RANGE.value(), ENOENT);
+        errorToErrnoMapper.put(ERROR_NESTING_NOT_ALLOWED.value(), EAGAIN);
+        // ERROR_PIPE_LOCAL (in MRI)
+        errorToErrnoMapper.put(229, EPIPE);
+        errorToErrnoMapper.put(ERROR_BAD_PIPE.value(), EPIPE);
+        errorToErrnoMapper.put(ERROR_PIPE_BUSY.value(), EAGAIN);
+        errorToErrnoMapper.put(ERROR_NO_DATA.value(), EPIPE);
+        errorToErrnoMapper.put(ERROR_PIPE_NOT_CONNECTED.value(), EPIPE);
+        errorToErrnoMapper.put(ERROR_OPERATION_ABORTED.value(), EINTR);
+        errorToErrnoMapper.put(ERROR_NOT_ENOUGH_QUOTA.value(), ENOMEM);
+        errorToErrnoMapper.put(ERROR_MOD_NOT_FOUND.value(), ENOENT);
+        errorToErrnoMapper.put(WSAENAMETOOLONG.value(), ENAMETOOLONG);
+        errorToErrnoMapper.put(WSAENOTEMPTY.value(), ENOTEMPTY);
+        errorToErrnoMapper.put(WSAEINTR.value(), EINTR);
+        errorToErrnoMapper.put(WSAEBADF.value(), EBADF);
+        errorToErrnoMapper.put(WSAEACCES.value(), EACCES);
+        errorToErrnoMapper.put(WSAEFAULT.value(), EFAULT);
+        errorToErrnoMapper.put(WSAEINVAL.value(), EINVAL);
+        errorToErrnoMapper.put(WSAEMFILE.value(), EMFILE);
+    }
+
+    private final FileStat checkFdStat;
+
+    WindowsPOSIX(LibCProvider libc, POSIXHandler handler) {
+        super(libc, handler);
+        this.checkFdStat = new WindowsFileStat(this);
+    }
+    
+    @Override
+    public FileStat allocateStat() {
+        return new WindowsRawFileStat(this, handler);
+    }
+
+    public MsgHdr allocateMsgHdr() {
+        handler.unimplementedError(MethodName.getCallerMethodName());
+        return null;
+    }
+
+    public SocketMacros socketMacros() {
+        handler.unimplementedError(MethodName.getCallerMethodName());
+        return null;
+    }
+
+    @Override
+    public int kill(int pid, int signal) {
+        handler.unimplementedError("kill");
+
+        return -1;
+    }
+
+    @Override
+    public int kill(long pid, int signal) {
+        handler.unimplementedError("kill");
+
+        return -1;
+    }
+
+    @Override
+    public int chmod(String filename, int mode) {
+        return wlibc()._wchmod(WString.path(filename), mode);
+    }
+
+    @Override
+    public int chdir(String path) {
+        return wlibc()._wchdir(WString.path(path));
+    }
+    
+    @Override
+    public int chown(String filename, int user, int group) {
+        return 0;
+    }
+    
+    @Override
+    public int exec(String path, String[] argv) {
+        if (argv.length == 1) return spawn(true, argv[0], null, path, null);
+
+        return aspawn(true, null, argv, path, null);
+    }
+
+    @Override
+    public CharSequence crypt(CharSequence key, CharSequence salt) {
+        return JavaLibCHelper.crypt(key, salt);
+    }
+
+    @Override
+    public byte[] crypt(byte[] key, byte[] salt) {
+        return JavaLibCHelper.crypt(key, salt);
+    }
+
+    @Override
+    public int exec(String path, String[] argv, String[] envp) {
+        if (argv.length == 1) return spawn(true, argv[0], null, path, envp);
+
+        return aspawn(true, null, argv, path, envp);
+    }
+    
+    @Override
+    public int execv(String path, String[] argv) {
+        handler.unimplementedError("egid");
+
+        return -1;
+    }
+
+    @Override
+    public int getegid() {
+        handler.unimplementedError("egid");
+
+        return -1;
+    }
+
+    @Override
+    public int setegid(int egid) {
+        handler.unimplementedError("setegid");
+
+        return -1;
+    }
+
+    @Override
+    public int geteuid() {
+        return 0;
+    }
+
+    @Override
+    public int seteuid(int euid) {
+        handler.unimplementedError("seteuid");
+
+        return -1;
+    }
+
+    @Override
+    public int getuid() {
+        return 0;
+    }
+
+    @Override
+    public int setuid(int uid) {
+        handler.unimplementedError("setuid");
+
+        return -1;
+    }
+
+    @Override
+    public int getgid() {
+        return 0;
+    }
+
+    @Override
+    public int setgid(int gid) {
+        handler.unimplementedError("setgid");
+
+        return -1;
+    }
+
+    @Override
+    public int getpgid(int pid) {
+        handler.unimplementedError("getpgid");
+
+        return -1;
+    }
+    
+    @Override
+    public int getpgid() {
+        handler.unimplementedError("getpgid");
+
+        return -1;
+    }
+
+    @Override
+    public int setpgid(int pid, int pgid) {
+        handler.unimplementedError("setpgid");
+
+        return -1;
+    }
+    
+    @Override
+    public int getpriority(int which, int who) {
+        handler.unimplementedError("getpriority");
+
+        return -1;
+    }
+    
+    @Override
+    public int setpriority(int which, int who, int prio) {
+        handler.unimplementedError("setpriority");
+
+        return -1;
+    }
+
+    @Override
+    public int getpid(){
+        return wlibc()._getpid();
+    }
+
+    @Override
+    public int getppid() {
+        return 0;
+    }
+    
+    @Override
+    public int lchmod(String filename, int mode) {
+        handler.unimplementedError("lchmod");
+        
+        return -1;
+    }
+    
+    @Override
+    public int lchown(String filename, int user, int group) {
+        handler.unimplementedError("lchown");
+        
+        return -1;
+    }
+
+    @Override
+    public String gethostname() {
+        ByteBuffer buffer = ByteBuffer.allocate(64);
+        IntByReference len = new IntByReference(buffer.capacity() - 1);
+        if (!wlibc().GetComputerNameW(buffer, len)) return helper.gethostname();
+        buffer.limit(len.intValue() * 2);
+        return Charset.forName("UTF-16LE").decode(buffer).toString();
+    }
+    
+    public FileStat fstat(int fd) {
+        WindowsFileStat stat = new WindowsFileStat(this);
+        if (fstat(fd, stat) < 0) handler.error(Errno.valueOf(errno()), "fstat", "" + fd);
+        return stat;
+    }
+    
+    @Override
+    public int fstat(FileDescriptor fileDescriptor, FileStat stat) {
+        WindowsByHandleFileInformation info = new WindowsByHandleFileInformation(getRuntime());
+        if (wlibc().GetFileInformationByHandle(JavaLibCHelper.gethandle(fileDescriptor), info) == 0) return -1;
+
+        ((WindowsRawFileStat) stat).setup(info);
+
+        return 0;
+    }
+    
+    @Override
+    public FileStat lstat(String path) {
+        return stat(path);
+    }
+
+    @Override
+    public int lstat(String path, FileStat stat) {
+        return stat(path, stat); // windows stat honors windows equiv of softlinks and dangling ones.
+    }
+
+    @Override
+    public int stat(String path, FileStat stat) {
+        WindowsFileInformation info = new WindowsFileInformation(getRuntime());
+        byte[] wpath = WString.path(path, true);
+
+        if (wlibc().GetFileAttributesExW(wpath, 0, info) != 0) {
+            ((WindowsRawFileStat) stat).setup(path, info);
+        } else {
+            int e = errno();
+
+            if (e == ERROR_FILE_NOT_FOUND.intValue() || e == ERROR_PATH_NOT_FOUND.intValue()
+                    || e == ERROR_BAD_NETPATH.intValue()) {
+                return -1;
+            }
+
+            return findFirstFile(path, stat);
+        }
+
+        return 0;
+    }
+
+    // Public so we can test this via unit-testing.  This makes me wish we had a whole different interface for
+    // windows APIs user32/kernel32 that this class could consume easily.  We are clearly missing an abstraction
+    // or project here.
+    public int findFirstFile(String path, FileStat stat) {
+        byte[] wpath = WString.path(path, true);
+        WindowsFindData findData = new WindowsFindData(getRuntime());
+        HANDLE handle = wlibc().FindFirstFileW(wpath, findData);
+        if (!handle.isValid()) return -1;
+        wlibc().FindClose(handle);
+        ((WindowsRawFileStat) stat).setup(path, findData);
+
+        return 0;
+    }
+
+    @Override
+    public String readlink(String oldpath) {
+        handler.unimplementedError("readlink");
+
+        return null;
+    }
+
+    @Override
+    public Pointer environ() {
+        return getRuntime().getMemoryManager().newPointer(wlibc()._environ().get());
+    }
+
+    @Override
+    public int setenv(String envName, String envValue, int overwrite) {
+        if (envName.contains("=")) {
+            handler.error(EINVAL, "setenv", envName);
+            return -1;
+        }
+
+        // FIXME: We do not have getenv implemented yet.  So we are ignoring for now
+        // POSIX specified.  Existence is success if overwrite is 0.
+        // if (overwrite == 0 && getenv(envName) != null) return 0;
+        
+        if (!wlibc().SetEnvironmentVariableW(new WString(envName), new WString(envValue))) {
+            handler.error(EINVAL, "setenv", envName);
+            return -1;
+        }
+
+        return 0;
+    }
+
+    @Override
+    public int umask(int mask) {
+        return wlibc()._umask(mask);
+    }
+    
+    @Override
+    public int unsetenv(String envName) {
+        if (!wlibc().SetEnvironmentVariableW(new WString(envName), null)) {
+            handler.error(EINVAL, "unsetenv", envName);
+            return -1;
+        }
+        
+        return 0;
+    }
+
+
+
+    private static final int GENERIC_ALL = 0x10000000;
+    private static final int GENERIC_READ = 0x80000000;
+    private static final int GENERIC_WRITE = 0x40000000;
+    private static final int GENERIC_EXECUTE = 0x2000000;
+
+    private static final int FILE_SHARE_DELETE = 0x00000004;
+    private static final int FILE_SHARE_READ =  0x00000001;
+    private static final int FILE_SHARE_WRITE =  0x00000002;
+
+    private static final int CREATE_ALWAYS = 2;
+    private static final int CREATE_NEW = 1;
+    private static final int OPEN_ALWAYS = 4;
+    private static final int OPEN_EXISTING = 3;
+    private static final int TRUNCATE_EXISTING = 5;
+
+    public static final int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
+
+    @Override
+    public int utimes(String path, long[] atimeval, long[] mtimeval) {
+        FileTime aTime = timevalToFileTime(atimeval);
+        FileTime mTime = timevalToFileTime(mtimeval);
+        return setFileTime(path, aTime, mTime);
+    }
+
+    @Override
+    public int utimensat(int dirfd, String path, long[] atimespec, long[] mtimespec, int flag) {
+        FileTime aTime = timespecToFileTime(atimespec);
+        FileTime mTime = timespecToFileTime(mtimespec);
+        return setFileTime(path, aTime, mTime);
+    }
+
+    private FileTime timevalToFileTime(long[] timeval) {
+        if (timeval == null) {
+            return currentFileTime();
+        }
+
+        // timeval unit is (sec, microsec)
+        long unixEpochIn100ns = timeval[0] * 10000000 + timeval[1] * 10;
+        return unixTimeToFileTime(unixEpochIn100ns);
+    }
+
+    private FileTime timespecToFileTime(long[] timespec) {
+        if (timespec == null) {
+            return currentFileTime();
+        }
+
+        // timespec unit is (sec, nanosec)
+        long unixEpochIn100ns = timespec[0] * 10000000 + timespec[1] / 100;
+        return unixTimeToFileTime(unixEpochIn100ns);
+    }
+
+    private int setFileTime(String path, FileTime aTime, FileTime mTime) {
+        byte[] wpath = WindowsHelpers.toWPath(path);
+        HANDLE handle = wlibc().CreateFileW(wpath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
+                null, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
+        if (!handle.isValid()) {
+            return -1;             // TODO proper error handling
+        }
+
+        boolean timeSet = wlibc().SetFileTime(handle, null, aTime, mTime);
+        wlibc().CloseHandle(handle);
+
+        return timeSet ? 0 : -1;
+    }
+
+    /**
+     *
+     * @param unixEpochIn100ns epoch time in 100-ns precision
+     * @return associated FILETIME structure
+     */
+    private FileTime unixTimeToFileTime(long unixEpochIn100ns) {
+        // FILETIME is a 64-bit unsigned integer representing
+        // the number of 100-nanosecond intervals since January 1, 1601
+        // UNIX timestamp is number of seconds since January 1, 1970
+        // 116444736000000000 = 10_000_000 * 60 * 60 * 24 * 365 * 369 + 89 leap days
+        long ft = 116444736000000000L + unixEpochIn100ns;
+
+        //long ft = CommonFileInformation.asNanoSeconds(unixTimeSeconds);
+        FileTime fileTime = new FileTime(getRuntime());
+        fileTime.dwLowDateTime.set(ft & 0xFFFFFFFFL);
+        fileTime.dwHighDateTime.set((ft >> 32) & 0xFFFFFFFFL);
+        return fileTime;
+    }
+
+    private FileTime nullFileTime() {
+        FileTime fileTime = new FileTime(getRuntime());
+        fileTime.dwLowDateTime.set(0);
+        fileTime.dwHighDateTime.set(0);
+        return fileTime;
+    }
+
+    private FileTime currentFileTime() {
+      return unixTimeToFileTime(System.currentTimeMillis() * 10000);
+    }
+
+    @Override
+    public int wait(int[] status) {
+        handler.unimplementedError("wait");
+
+        return -1;
+    }
+
+    @Override
+    public int waitpid(int pid, int[] status, int flags) {
+        if (pid <= 0) {
+            handler.unimplementedError("waitpid");
+        }
+
+        HANDLE h = wlibc().OpenProcess(WindowsLibC.PROCESS_QUERY_INFORMATION, 0, pid);
+        if (h == null) {
+            return -1; // TODO: Throw exception
+        }
+
+        // Block
+        if ((flags & WaitFlags.WNOHANG.intValue()) != 0) {
+            wlibc().WaitForSingleObject(h, WindowsLibC.INFINITE);
+        }
+
+        IntByReference exitCode = new IntByReference();
+        wlibc().GetExitCodeProcess(h, exitCode);
+        wlibc().CloseHandle(h);
+        int code = exitCode.getValue();
+        if (code == 259) {
+            return 0;
+        } else {
+            status[0] = code;
+            return pid;
+        }
+    }
+
+    @Override
+    public int waitpid(long pid, int[] status, int flags) {
+        if (pid > Integer.MAX_VALUE) {
+            throw new java.lang.IllegalArgumentException("waitpid");
+        }
+        return waitpid((int) pid, status, flags);
+    }
+
+    @Override
+    public String getlogin() {
+        return helper.getlogin();
+    }
+
+    @Override
+    public int endgrent() {
+        return 0;
+    }
+
+    @Override
+    public int endpwent() {
+        return helper.endpwent();
+    }
+
+    @Override
+    public Group getgrent() {
+        return null;
+    }
+
+    @Override
+    public Passwd getpwent() {
+        return null;
+    }
+
+    @Override
+    public Group getgrgid(int which) {
+        return null;
+    }
+
+    @Override
+    public Passwd getpwnam(String which) {
+        return null;
+    }
+
+    @Override
+    public Group getgrnam(String which) {
+        return null;
+    }
+    
+    @Override
+    public int setgrent() {
+        return 0;
+    }
+    
+    @Override
+    public int setpwent() {
+        return helper.setpwent();
+    }
+
+    @Override
+    public Passwd getpwuid(int which) {
+        return null;
+    }
+
+    @Override
+    public boolean isatty(FileDescriptor fd) {
+        HANDLE handle = JavaLibCHelper.gethandle(fd);
+
+        int type = wlibc().GetFileType(handle);
+        return type == FILE_TYPE_CHAR;
+    }
+
+    @Override
+    public int isatty(int fd) {
+        HANDLE handle = JavaLibCHelper.gethandle(fd);
+
+        int type = wlibc().GetFileType(handle);
+        return type == FILE_TYPE_CHAR ? 1 : 0;
+    }
+
+    @Override
+    public int mkdir(String path, int mode) {
+        WString widePath = WString.path(path);
+        int res = -1;
+        
+        if (wlibc()._wmkdir(widePath) == 0) {
+            res = wlibc()._wchmod(widePath, mode);
+        }
+        
+        if (res < 0) {
+            int errno = errno();
+            handler.error(Errno.valueOf(errno), "mkdir", path);
+        }
+        return res;
+    }
+    
+    // FIXME: Should this and other fields be part of constantine/jnr-constants?
+    static final int FILE_ATTRIBUTE_READONLY = 1;
+    static final int INVALID_FILE_ATTRIBUTES = -1;
+    
+    /**
+     * The logic here is a bit strange and this copies MRI (Ruby) which may not be language
+     * agnostic, but windows (win7 and others) automatically mark folders as read-only when 
+     * it contains other files and folders within it.  This document explains more: 
+     * http://support.microsoft.com/kb/326549
+     * I think the logic is based around idea that if you removed all other files it would
+     * be empty but will stay marked as read-only.
+     *
+     * @param path the path to remove
+     * @return 0 if successful, -1 if failed
+     */
+    @Override
+    public int rmdir(String path) {
+        WString pathW = WString.path(path);
+        int attr = wlibc().GetFileAttributesW(pathW);
+        boolean isReadOnly = attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_READONLY) != 0;
+        
+        if (isReadOnly) wlibc().SetFileAttributesW(pathW, attr & ~FILE_ATTRIBUTE_READONLY);
+        
+        if (!wlibc().RemoveDirectoryW(pathW)) {
+            int errno = errno();
+
+            if (isReadOnly) wlibc().SetFileAttributesW(pathW, attr & FILE_ATTRIBUTE_READONLY);
+            
+            handler.error(mapErrorToErrno(errno), "rmdir", path);
+            
+            return -1;
+        }
+
+        return 0;
+    }
+
+    @Override
+    public int link(String oldpath, String newpath) {
+        boolean linkCreated =  wlibc().CreateHardLinkW(WString.path(newpath), WString.path(oldpath), null);
+
+        if (!linkCreated) {
+            int error = errno();
+            handler.error(mapErrorToErrno(error), "link", oldpath + " or " + newpath);
+            return error;
+        } else {
+            return 0;
+        }
+    }
+    
+    /**
+     * @param overlay is P_OVERLAY if true and P_NOWAIT if false
+     * @param program to be invoked
+     * @param argv is all args including argv0 being what is executed
+     * @param path is path to be searched when needed (delimited by ; on windows)
+     * @param envp is a set of KEY=VALUE strings to be set in the child process
+     * @return the pid
+     */    
+    public int aspawn(boolean overlay, String program, String[] argv, String path, String[] envp) {
+        try {
+        if (argv.length == 0) return -1;
+        
+        String[] cmds = WindowsHelpers.processCommandArgs(this, program, argv, path);
+ 
+        return childResult(createProcess("aspawn", cmds[0], cmds[1], null, null, null, null, envp), overlay);
+        } catch (Exception e) {
+            return -1;
+        }
+    }
+
+    public int pipe(int[] fds) {
+        // TODO (nirvdrum 06-May-15) Maybe not hard-code the psize value. But figure out a sensible way to handle textmode.
+        return ((WindowsLibC) libc())._pipe(fds, 512, 0);
+    }
+
+    public int truncate(CharSequence path, long length) {
+        // Windows doesn't have a native truncate() equivalent, but it does have a native ftruncate() equivalent.
+        // In order to call the ftruncate() equivalent, we must convert a path to a FD.  We do that by wrapping the
+        // ftruncate() call with open() and close().
+
+        // Permissions are ignored since we're not using O_CREAT.
+        int fd = libc().open(path, OpenFlags.O_WRONLY.intValue(), 0);
+        if (fd == -1) {
+            return -1;
+        }
+
+        if (libc().ftruncate(fd, length) == -1) {
+            return -1;
+        }
+
+        if (libc().close(fd) == -1) {
+            return -1;
+        }
+
+        // truncate() returns 0 on success.
+        return 0;
+    }
+
+    public int fcntlInt(int fd, Fcntl fcntl, int arg) {
+        switch(fcntl) {
+            case F_GETFD: {
+                if (checkFd(fd) == -1) {
+                    return -1;
+                } else {
+                    // This is a gigantic hack.  Indicate that Windows does not support close-on-exec.
+                    return 0;
+                }
+            }
+
+            case F_SETFD: {
+                if (checkFd(fd) == -1) {
+                    return -1;
+                } else {
+                    // This is a gigantic hack.  Indicate that Windows does not support close-on-exec by no-oping.
+                    return 0;
+                }
+            }
+
+            case F_GETFL: {
+                if (checkFd(fd) == -1) {
+                    return -1;
+                } else {
+                    // TODO (nirvdrum 06-May-15): Look up the actual flags rather than optimistically hard-coding this set.
+                    return OpenFlags.O_RDWR.intValue();
+                }
+            }
+
+            default: {
+                handler.unimplementedError("fcntl");
+
+                return -1;
+            }
+        }
+    }
+    
+    private WindowsLibC wlibc() {
+        return (WindowsLibC) libc();
+    }
+    
+    /**
+     * @param overlay is P_OVERLAY if true and P_NOWAIT if false
+     * @param command full command string
+     * @param program program to be invoked
+     * @param path is path to be searched when needed (delimited by ; on windows)
+     * @param envp is a set of KEY=VALUE strings to be set in the child process
+     * @return the pid
+     */
+    public int spawn(boolean overlay, String command, String program, String path, String[] envp) {
+        if (command == null) return -1;
+
+        String[] cmds = WindowsHelpers.processCommandLine(this, command, program, path);
+
+        return childResult(createProcess("spawn", cmds[0], cmds[1], null, null, null, null, envp), overlay);
+    }
+    
+    private int childResult(WindowsChildRecord child, boolean overlay) {
+        if (child == null) return -1;
+
+        if (overlay) {
+            IntByReference exitCode = new IntByReference();
+
+            WindowsLibC libc = (WindowsLibC) libc();
+            HANDLE handle = child.getProcess();
+            
+            libc.WaitForSingleObject(handle, WindowsLibC.INFINITE);
+            libc.GetExitCodeProcess(handle, exitCode);
+            libc.CloseHandle(handle);
+            System.exit(exitCode.getValue());
+        }
+
+        return child.getPid();
+    }
+
+    private static Errno mapErrorToErrno(int error) {
+        Errno errno = errorToErrnoMapper.get(error);
+        if (errno == null) {
+            errno = __UNKNOWN_CONSTANT__;
+        }
+        return errno;
+    }
+
+    private static final int STARTF_USESTDHANDLES = 0x00000100;
+    
+    // Used by spawn and aspawn (Note: See fixme below...envp not hooked up yet)
+    private WindowsChildRecord createProcess(String callingMethodName, String command, String program, 
+            WindowsSecurityAttributes securityAttributes, HANDLE input,
+            HANDLE output, HANDLE error, String[] envp) {
+        if (command == null && program == null) {
+            handler.error(EFAULT, callingMethodName, "no command or program specified");
+            return null;
+        }
+        
+        if (securityAttributes == null) {
+            securityAttributes = new WindowsSecurityAttributes(getRuntime());
+        }
+        
+        WindowsStartupInfo startupInfo = new WindowsStartupInfo(getRuntime());
+        
+        startupInfo.setFlags(STARTF_USESTDHANDLES);
+        startupInfo.setStandardInput(input != null ? input :
+                wlibc().GetStdHandle(WindowsLibC.STD_INPUT_HANDLE));
+        startupInfo.setStandardOutput(output != null ? output :
+                wlibc().GetStdHandle(WindowsLibC.STD_OUTPUT_HANDLE));
+        startupInfo.setStandardError(error != null ? input :
+                wlibc().GetStdHandle(WindowsLibC.STD_ERROR_HANDLE));
+        
+        int creationFlags = WindowsLibC.NORMAL_PRIORITY_CLASS | WindowsLibC.CREATE_UNICODE_ENVIRONMENT;
+        WindowsProcessInformation processInformation = new WindowsProcessInformation(getRuntime());
+
+        // FIXME: Convert envp into useful wideEnv
+        Pointer wideEnv = null;
+        byte[] programW = WindowsHelpers.toWString(program);
+        byte[] cwd = WindowsHelpers.toWString(WindowsHelpers.escapePath(handler.getCurrentWorkingDirectory().toString()) +"\\");
+        ByteBuffer commandW = ByteBuffer.wrap(WindowsHelpers.toWString(command));
+        boolean returnValue = wlibc().CreateProcessW(programW, commandW, 
+                securityAttributes, securityAttributes, 
+                securityAttributes.getInheritHandle() ? 1: 0, creationFlags, wideEnv, cwd, 
+                startupInfo, processInformation);
+        
+        if (!returnValue) return null;
+        
+        wlibc().CloseHandle(processInformation.getThread());
+        
+        // TODO: On winnt reverse sign of pid
+        return new WindowsChildRecord(processInformation.getProcess(), processInformation.getPid());
+    }
+
+    private int checkFd(int fd) {
+        // There might be a lighter-weight check, but we basically want to
+        // make sure the FD is valid and the effective user can access it,
+        // since we need to simulate fcntl semantics.
+        return libc().fstat(fd, checkFdStat);
+    }
+
+    public static final PointerConverter PASSWD = new PointerConverter() {
+        public Object fromNative(Object arg, FromNativeContext ctx) {
+            throw new RuntimeException("no support for native passwd");
+        }
+    };
+
+    public int mkfifo(String filename, int mode) {
+        handler.unimplementedError("mkfifo");
+
+        return -1;
+    }
+
+    public Timeval allocateTimeval() {
+        return new DefaultNativeTimeval(getRuntime());
+    }
+
+    // TODO: Replace with Win32 calls. See jnr/jnr-posix#98.
+    public int gettimeofday(Timeval tv) {
+        long currentMillis = System.currentTimeMillis();
+        tv.sec(currentMillis / 1000);
+        tv.usec(currentMillis % 1000 * 1000);
+        return 0;
+    }
+}
diff --git a/src/main/java/jnr/posix/WindowsProcessInformation.java b/src/main/java/jnr/posix/WindowsProcessInformation.java
new file mode 100644
index 0000000..e2a8035
--- /dev/null
+++ b/src/main/java/jnr/posix/WindowsProcessInformation.java
@@ -0,0 +1,33 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package jnr.posix;
+
+/**
+ *
+ * @author enebo
+ */
+public class WindowsProcessInformation extends jnr.ffi.Struct {
+    final Pointer hProcess = new Pointer();
+    final Pointer hThread = new Pointer();
+    final Unsigned32 dwProcessId = new Unsigned32();
+    final Unsigned32 dwThreadId = new Unsigned32();
+
+    public WindowsProcessInformation(jnr.ffi.Runtime runtime) {
+        super(runtime);
+    }
+    
+    public HANDLE getThread() {
+        return new HANDLE(hThread.get());
+    }
+    
+    public HANDLE getProcess() {
+        return new HANDLE(hProcess.get());
+    }
+    
+    public int getPid() {
+        return dwProcessId.intValue();
+    }
+}
diff --git a/src/main/java/jnr/posix/WindowsRawFileStat.java b/src/main/java/jnr/posix/WindowsRawFileStat.java
new file mode 100644
index 0000000..8c0a048
--- /dev/null
+++ b/src/main/java/jnr/posix/WindowsRawFileStat.java
@@ -0,0 +1,212 @@
+package jnr.posix;
+
+import jnr.posix.util.WindowsHelpers;
+import jnr.posix.windows.CommonFileInformation;
+
+public class WindowsRawFileStat extends AbstractJavaFileStat implements NanosecondFileStat {
+    private int st_atime;
+    private long st_atimensec;
+    private long st_mtimensec;
+    private long st_ctimensec;
+    private int st_rdev;
+    private int st_dev;
+    private int st_nlink;
+    private int st_mode;
+    private long st_size;
+    private int st_ctime;
+    private int st_mtime;
+
+    public WindowsRawFileStat(POSIX posix, POSIXHandler handler) {
+        super(posix, handler);
+    }
+
+    public void setup(String path, CommonFileInformation fileInfo) {
+        st_mode = fileInfo.getMode(path);
+        setup(fileInfo);
+
+        if (WindowsHelpers.isDriveLetterPath(path)) {
+            int letterAsNumber = Character.toUpperCase(path.charAt(0)) - 'A';
+            st_rdev = letterAsNumber;
+            st_dev = letterAsNumber;
+        }
+    }
+
+    public void setup(CommonFileInformation fileInfo) {
+        long atime = fileInfo.getLastAccessTimeNanoseconds();
+        st_atimensec = atime % CommonFileInformation.NANOSECONDS;
+        st_atime = (int) (atime / CommonFileInformation.NANOSECONDS);
+        long mtime = fileInfo.getLastWriteTimeNanoseconds();
+        st_mtimensec = mtime % CommonFileInformation.NANOSECONDS;
+        st_mtime = (int) (mtime / CommonFileInformation.NANOSECONDS);
+        long ctime = fileInfo.getCreationTimeNanoseconds();
+        st_ctimensec = ctime % CommonFileInformation.NANOSECONDS;
+        st_ctime = (int) (ctime / CommonFileInformation.NANOSECONDS);
+        st_size = isDirectory() ? 0 : fileInfo.getFileSize();
+        st_nlink = 1;
+        st_mode &= ~(S_IWGRP | S_IWOTH);
+    }
+
+    public int mode() {
+        return st_mode;
+    }
+
+    @Override
+    public long mtime() {
+        return st_mtime;
+    }
+
+    public long atime() {
+        return st_atime;
+    }
+
+    @Override
+    public long aTimeNanoSecs() {
+        return st_atimensec;
+    }
+
+    @Override
+    public long cTimeNanoSecs() {
+        return st_ctimensec;
+    }
+
+    @Override
+    public long mTimeNanoSecs() {
+        return st_mtimensec;
+    }
+
+    public long dev() {
+        return st_dev;
+    }
+
+    public int nlink() {
+        return st_nlink;
+    }
+
+    public long rdev() {
+        return st_rdev;
+    }
+
+    @Override
+    public long st_size() {
+        return st_size;
+    }
+
+    @Override
+    public long ctime() {
+        return st_ctime;
+    }
+
+    public boolean isDirectory() {
+        return (mode() & S_IFMT) == S_IFDIR;
+    }
+
+    public boolean isEmpty() {
+        return st_size() == 0;
+    }
+
+    @Override
+    public boolean isExecutable() {
+        if (isOwned()) return (mode() & S_IXUSR) != 0;
+        if (isGroupOwned()) return (mode() & S_IXGRP) != 0;
+        if ((mode() & S_IXOTH) != 0) return false;
+
+        return true;
+    }
+
+    @Override
+    public boolean isExecutableReal() {
+        if (isROwned()) return (mode() & S_IXUSR) != 0;
+        if (groupMember(gid())) return (mode() & S_IXGRP) != 0;
+        if ((mode() & S_IXOTH) != 0) return false;
+
+        return true;
+    }
+
+    public boolean isFile() {
+        return (mode() & S_IFMT) == S_IFREG;
+    }
+
+    public boolean isFifo() {
+        return (mode() & S_IFMT) == S_IFIFO;
+    }
+
+    public boolean isGroupOwned() {
+        return groupMember(gid());
+    }
+
+    public boolean isIdentical(FileStat other) {
+        return dev() == other.dev() && ino() == other.ino();
+    }
+
+    public boolean isNamedPipe() {
+        return (mode() & S_IFIFO) != 0;
+    }
+
+    // FIXME: Implement
+    @Override
+    public boolean isOwned() {
+        return true;
+    }
+
+    // FIXME: Implement
+    @Override
+    public boolean isROwned() {
+        return true;
+    }
+
+    @Override
+    public boolean isReadable() {
+        if (isOwned()) return (mode() & S_IRUSR) != 0;
+        if (isGroupOwned()) return (mode() & S_IRGRP) != 0;
+        if ((mode() & S_IROTH) != 0) return false;
+
+        return true;
+    }
+
+    @Override
+    public boolean isReadableReal() {
+        if (isROwned()) return (mode() & S_IRUSR) != 0;
+        if (groupMember(gid())) return (mode() & S_IRGRP) != 0;
+        if ((mode() & S_IROTH) != 0) return false;
+
+        return true;
+    }
+
+    public boolean isSetgid() {
+        return (mode() & S_ISGID) != 0;
+    }
+
+    public boolean isSetuid() {
+        return (mode() & S_ISUID) != 0;
+    }
+
+    public boolean isSocket() {
+        return (mode() & S_IFMT) == S_IFSOCK;
+    }
+
+    public boolean isSticky() {
+        return (mode() & S_ISVTX) != 0;
+    }
+
+    public boolean isSymlink() {
+        return (mode() & S_IFMT) == S_IFLNK;
+    }
+
+    @Override
+    public boolean isWritable() {
+        if (isOwned()) return (mode() & S_IWUSR) != 0;
+        if (isGroupOwned()) return (mode() & S_IWGRP) != 0;
+        if ((mode() & S_IWOTH) != 0) return false;
+
+        return true;
+    }
+
+    @Override
+    public boolean isWritableReal() {
+        if (isROwned()) return (mode() & S_IWUSR) != 0;
+        if (groupMember(gid())) return (mode() & S_IWGRP) != 0;
+        if ((mode() & S_IWOTH) != 0) return false;
+
+        return true;
+    }
+}
diff --git a/src/main/java/jnr/posix/WindowsSecurityAttributes.java b/src/main/java/jnr/posix/WindowsSecurityAttributes.java
new file mode 100644
index 0000000..a2a4b33
--- /dev/null
+++ b/src/main/java/jnr/posix/WindowsSecurityAttributes.java
@@ -0,0 +1,28 @@
+package jnr.posix;
+
+import jnr.ffi.*;
+
+/**
+ *
+ */
+public class WindowsSecurityAttributes extends jnr.ffi.Struct {
+    public final Unsigned32 length = new Unsigned32();
+    public final Pointer securityDescriptor = new Pointer();
+    public final WBOOL inheritHandle = new WBOOL();
+
+    public WindowsSecurityAttributes(jnr.ffi.Runtime runtime) {
+        super(runtime);
+        
+        // This seems like the sensible defaults for this.
+        length.set(Struct.size(this));
+        inheritHandle.set(true);
+    }
+    
+    public long getLength() {
+        return length.get();
+    }
+
+    public boolean getInheritHandle() {
+        return inheritHandle.get();
+    }
+}
diff --git a/src/main/java/jnr/posix/WindowsStartupInfo.java b/src/main/java/jnr/posix/WindowsStartupInfo.java
new file mode 100644
index 0000000..1058aff
--- /dev/null
+++ b/src/main/java/jnr/posix/WindowsStartupInfo.java
@@ -0,0 +1,51 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package jnr.posix;
+
+/**
+ *
+ * @author enebo
+ */
+public class WindowsStartupInfo extends jnr.ffi.Struct {
+    public final Unsigned32  cb = new Unsigned32();
+    public final Pointer lpReserved = new Pointer(); //new UTF8String();
+    public final Pointer lpDesktop = new Pointer(); //UTF8String();
+    public final Pointer lpTitle = new Pointer(); //new UTF8String();
+    public final Unsigned32  dwX = new Unsigned32();
+    public final Unsigned32  dwY = new Unsigned32();
+    public final Unsigned32  dwXSize = new Unsigned32();
+    public final Unsigned32  dwYSize = new Unsigned32();
+    public final Unsigned32  dwXCountChars = new Unsigned32();
+    public final Unsigned32  dwYCountChars = new Unsigned32();
+    public final Unsigned32  dwFillAttribute = new Unsigned32();
+    public final Unsigned32  dwFlags = new Unsigned32();
+    public final Unsigned16   wShowWindow = new Unsigned16();
+    public final Unsigned16   cbReserved2 = new Unsigned16();
+    public final Pointer lpReserved2 = new Pointer();
+    public final Pointer standardInput = new Pointer();
+    public final Pointer standardOutput = new Pointer();
+    public final Pointer standardError = new Pointer();
+  
+    public WindowsStartupInfo(jnr.ffi.Runtime runtime) {
+        super(runtime);
+    }
+  
+    public void setFlags(int value) {
+        dwFlags.set(value);
+    }
+
+    public void setStandardInput(HANDLE standardInput) {
+        this.standardInput.set(standardInput.toPointer());
+    }
+
+    public void setStandardOutput(HANDLE standardOutput) {
+        this.standardOutput.set(standardOutput.toPointer());
+    }
+
+    public void setStandardError(HANDLE standardError) {
+        this.standardError.set(standardError.toPointer());
+    }
+}
diff --git a/src/main/java/jnr/posix/util/Chmod.java b/src/main/java/jnr/posix/util/Chmod.java
new file mode 100644
index 0000000..202ea0a
--- /dev/null
+++ b/src/main/java/jnr/posix/util/Chmod.java
@@ -0,0 +1,101 @@
+package jnr.posix.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Honor semantics of chmod as best we can in pure Java.  Note, this uses reflection to be 
+ * more tolerant of different Java versions. 
+ */
+public class Chmod {
+    private static final boolean CHMOD_API_AVAILABLE;
+    private static final Method setWritable;
+    private static final Method setReadable;
+    private static final Method setExecutable;
+    
+    static {
+        boolean apiAvailable = false;
+        Method setWritableVar = null;
+        Method setReadableVar = null;
+        Method setExecutableVar = null;
+        try {
+            setWritableVar = File.class.getMethod("setWritable", new Class[] {Boolean.TYPE, Boolean.TYPE});
+            setReadableVar = File.class.getMethod("setReadable", new Class[] {Boolean.TYPE, Boolean.TYPE});
+            setExecutableVar = File.class.getMethod("setExecutable", new Class[] {Boolean.TYPE, Boolean.TYPE});
+            apiAvailable = true;
+        } catch (Exception e) {
+            // failed to load methods, no chmod API available
+        }
+        setWritable = setWritableVar;
+        setReadable = setReadableVar;
+        setExecutable = setExecutableVar;
+        CHMOD_API_AVAILABLE = apiAvailable;
+    }
+    
+    public static int chmod(File file, String mode) {
+        if (CHMOD_API_AVAILABLE) {
+            // fast version
+            char other = '0';
+            if (mode.length() >= 1) {
+                other = mode.charAt(mode.length() - 1);
+            }
+            //char group = mode.charAt(mode.length() - 2);
+            char user = '0';
+            if (mode.length() >= 3) {
+                user = mode.charAt(mode.length() - 3);
+            }
+            //char setuidgid = mode.charAt(mode.length() - 3);
+            
+            // group and setuid/gid are ignored, no way to do them fast. Should we fall back on slow?
+            if (!setPermissions(file, other, false)) return -1;
+            if (!setPermissions(file, user, true)) return -1;
+            return 0;
+        } else {
+            // slow version
+            try {
+                Process chmod = Runtime.getRuntime().exec("/bin/chmod " + mode + " " + file.getAbsolutePath());
+                chmod.waitFor();
+                return chmod.exitValue();
+            } catch (IOException ioe) {
+                // FIXME: ignore?
+            } catch (InterruptedException ie) {
+                Thread.currentThread().interrupt();
+            }
+        }
+        return -1;
+    }
+    
+    private static boolean setPermissions(File file, char permChar, boolean userOnly) {
+        int permValue = Character.digit(permChar, 8);
+        
+        try {
+            if ((permValue & 1) != 0) {
+                setExecutable.invoke(file, new Object[] {Boolean.TRUE, Boolean.valueOf(userOnly)});
+            } else {
+                setExecutable.invoke(file, new Object[] {Boolean.FALSE, Boolean.valueOf(userOnly)});
+            }
+            
+            if ((permValue & 2) != 0) {
+                setWritable.invoke(file, new Object[] {Boolean.TRUE, Boolean.valueOf(userOnly)});
+            } else {
+                setWritable.invoke(file, new Object[] {Boolean.FALSE, Boolean.valueOf(userOnly)});
+            }
+            
+            if ((permValue & 4) != 0) {
+                setReadable.invoke(file, new Object[] {Boolean.TRUE, Boolean.valueOf(userOnly)});
+            } else {
+                setReadable.invoke(file, new Object[] {Boolean.FALSE, Boolean.valueOf(userOnly)});
+            }
+            
+            return true;
+        } catch (IllegalAccessException iae) {
+            // ignore, return false below
+        } catch (InvocationTargetException ite) {
+            // ignore, return false below
+        }
+        
+        return false;
+    }
+}
diff --git a/src/main/java/jnr/posix/util/DefaultPOSIXHandler.java b/src/main/java/jnr/posix/util/DefaultPOSIXHandler.java
new file mode 100644
index 0000000..a5ef603
--- /dev/null
+++ b/src/main/java/jnr/posix/util/DefaultPOSIXHandler.java
@@ -0,0 +1,74 @@
+package jnr.posix.util;
+
+import jnr.constants.platform.Errno;
+import jnr.posix.POSIXHandler;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.Arrays;
+import java.util.IllegalFormatException;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A POSIXHandler with reasonable default behavior.
+ */
+public class DefaultPOSIXHandler implements POSIXHandler {
+    public void error(Errno error, String extraData) {
+        throw new RuntimeException("native error " + error.description() + " " + extraData);
+    }
+
+    public void error(Errno error, String methodName, String extraData) {
+        throw new RuntimeException("native error calling " + methodName + ": " + error.description() + " " + extraData);
+    }
+    
+    public void unimplementedError(String methodName) {
+        throw new IllegalStateException(methodName + " is not implemented in jnr-posix");
+    }
+
+    public void warn(WARNING_ID id, String message, Object... data) {
+        String msg;
+        try {
+            msg = String.format(message, data);
+        }
+        catch (IllegalFormatException e) {
+            msg = message + " " + Arrays.toString(data);
+        }
+        Logger.getLogger("jnr-posix").log(Level.WARNING, msg);
+    }
+
+    public boolean isVerbose() {
+        return false;
+    }
+
+    public File getCurrentWorkingDirectory() {
+        return new File(".");
+    }
+
+    public String[] getEnv() {
+        String[] envp = new String[System.getenv().size()];
+        int i = 0;
+        for (Map.Entry<String, String> pair : System.getenv().entrySet()) {
+            envp[i++] = new StringBuilder(pair.getKey()).append("=").append(pair.getValue()).toString();
+        }
+        return envp;
+    }
+
+    public InputStream getInputStream() {
+        return System.in;
+    }
+
+    public PrintStream getOutputStream() {
+        return System.out;
+    }
+
+    public int getPID() {
+        return 0;
+    }
+
+    public PrintStream getErrorStream() {
+        return System.err;
+    }
+}
diff --git a/src/main/java/jnr/posix/util/ExecIt.java b/src/main/java/jnr/posix/util/ExecIt.java
new file mode 100644
index 0000000..a79512f
--- /dev/null
+++ b/src/main/java/jnr/posix/util/ExecIt.java
@@ -0,0 +1,157 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: EPL 2.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Eclipse Public
+ * License Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * Copyright (C) 2008 JRuby Community
+ * 
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+
+package jnr.posix.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import jnr.posix.POSIXHandler;
+
+public class ExecIt {
+    protected final POSIXHandler handler;
+
+    /** Creates a new instance of ShellLauncher
+     *
+     * @param handler the {@link POSIXHandler} to use
+     */
+    public ExecIt(POSIXHandler handler) {
+        this.handler = handler;
+    }
+
+    public int runAndWait(String... args) throws IOException, InterruptedException {
+        return runAndWait(handler.getOutputStream(), args);
+    }
+
+    public int runAndWait(OutputStream output, String... args) throws IOException, InterruptedException {
+        return runAndWait(output, handler.getErrorStream(), args);
+    }
+
+    public int runAndWait(OutputStream output, OutputStream error, String... args) throws IOException, InterruptedException {
+        Process process = run(args);
+
+        handleStreams(process, handler.getInputStream(), output, error);
+
+        return process.waitFor();
+    }
+
+    public Process run(String... args) throws IOException {
+        File cwd = handler.getCurrentWorkingDirectory();
+          
+        return Runtime.getRuntime().exec(args, handler.getEnv(), cwd);        
+    }
+
+    private static class StreamPumper extends Thread {
+        private InputStream in;
+        private OutputStream out;
+        private boolean onlyIfAvailable;
+        private volatile boolean quit;
+        private final Object waitLock = new Object();
+        StreamPumper(InputStream in, OutputStream out, boolean avail) {
+            this.in = in;
+            this.out = out;
+            this.onlyIfAvailable = avail;
+        }
+        
+        public void run() {
+            byte[] buf = new byte[1024];
+            int numRead;
+            boolean hasReadSomething = false;
+            try {
+                while (!quit) {
+                    // The problem we trying to solve below: STDIN in Java is blocked and 
+                    // non-interruptible, so if we invoke read on it, we might never be able to
+                    // interrupt such thread.  So, we use in.available() to see if there is any 
+                    // input ready, and only then read it. But this approach can't tell whether 
+                    // the end of stream reached or not, so we might end up looping right at the
+                    // end of the stream.  Well, at least, we can improve the situation by checking
+                    // if some input was ever available, and if so, not checking for available 
+                    // anymore, and just go to read.
+                    if (onlyIfAvailable && !hasReadSomething) {
+                        if (in.available() == 0) {
+                            synchronized (waitLock) {
+                                waitLock.wait(10);                                
+                            }
+                            continue;
+                        } else {
+                            hasReadSomething = true;
+                        }
+                    }
+
+                    if ((numRead = in.read(buf)) == -1) {
+                        break;
+                    }
+                    out.write(buf, 0, numRead);
+                }
+            } catch (Exception e) {
+            } finally {
+                if (onlyIfAvailable) {
+                    // We need to close the out, since some
+                    // processes would just wait for the stream
+                    // to be closed before they process its content,
+                    // and produce the output. E.g.: "cat".
+                    try { out.close(); } catch (IOException ioe) {}
+                }                
+            }
+        }
+        
+        public void quit() {
+            this.quit = true;
+            synchronized (waitLock) {
+                waitLock.notify();                
+            }
+        }
+    }
+
+    private void handleStreams(Process p, InputStream in, OutputStream out, OutputStream err) throws IOException {
+        InputStream pOut = p.getInputStream();
+        InputStream pErr = p.getErrorStream();
+        OutputStream pIn = p.getOutputStream();
+
+        StreamPumper t1 = new StreamPumper(pOut, out, false);
+        StreamPumper t2 = new StreamPumper(pErr, err, false);
+        StreamPumper t3 = new StreamPumper(in, pIn, true);
+        t1.start();
+        t2.start();
+        t3.start();
+
+        try { t1.join(); } catch (InterruptedException ie) {}
+        try { t2.join(); } catch (InterruptedException ie) {}
+        t3.quit();
+
+        try { err.flush(); } catch (IOException io) {}
+        try { out.flush(); } catch (IOException io) {}
+
+        try { pIn.close(); } catch (IOException io) {}
+        try { pOut.close(); } catch (IOException io) {}
+        try { pErr.close(); } catch (IOException io) {}
+
+    }
+}
diff --git a/src/main/java/jnr/posix/util/FieldAccess.java b/src/main/java/jnr/posix/util/FieldAccess.java
new file mode 100644
index 0000000..bdbe093
--- /dev/null
+++ b/src/main/java/jnr/posix/util/FieldAccess.java
@@ -0,0 +1,31 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package jnr.posix.util;
+
+import java.lang.reflect.Field;
+
+/**
+ *
+ * @author nicksieger
+ */
+public class FieldAccess {
+    public static Field getProtectedField(Class klass, String fieldName) {
+        Field field = null;
+        try {
+            field = klass.getDeclaredField(fieldName);
+            field.setAccessible(true);
+        } catch (Exception e) {
+        }
+        return field;
+    }
+    public static Object getProtectedFieldValue(Class klass, String fieldName, Object instance) {
+        try {
+            Field f = getProtectedField(klass, fieldName);
+            return f.get(instance);
+        } catch (Exception e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+}
diff --git a/src/main/java/jnr/posix/util/Finder.java b/src/main/java/jnr/posix/util/Finder.java
new file mode 100644
index 0000000..85cc34e
--- /dev/null
+++ b/src/main/java/jnr/posix/util/Finder.java
@@ -0,0 +1,131 @@
+package jnr.posix.util;
+
+import java.io.File;
+import java.util.*;
+
+import jnr.posix.FileStat;
+import jnr.posix.POSIX;
+
+public class Finder {
+    private static final Collection<String> EXECUTABLE_EXTENSIONS
+            = Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(".exe", ".com", ".cmd", ".bat")));
+
+    public static String findFileInPath(POSIX posix, String name, String path) {
+        if (path == null || path.length() == 0) path = System.getenv("PATH");
+        
+        // MRI sets up a bogus path which seems like it would violate security
+        // if nothing else since if I don't have /usr/bin in my path but I end
+        // up executing it anyways???  Returning original name and hoping for 
+        // best.
+        if (path == null || path.length() == 0) return name;
+        
+        return findFileCommon(posix, name, path, true);
+    }
+    
+    public static String findFileCommon(POSIX posix, String name, String path, boolean executableOnly) {
+        // No point looking for nothing...
+        if (name == null || name.length() == 0) return name;
+        
+        int length = name.length();
+        boolean isAbsolute = false;
+        boolean isPath = false;
+        int i = 0;
+        if (Platform.IS_WINDOWS) {
+            if (length > 1 && Character.isLetter(name.charAt(0)) && name.charAt(1) == ':') {
+                i = 2;
+                isAbsolute = true;
+            }
+
+            int extensionIndex = -1;
+            char c = name.charAt(i);
+            if (i == '/' || i == '\\') {
+                i++;
+                c = name.charAt(i);
+                isAbsolute = true;
+            }
+
+            // Is this a partial path and does it contain an explicit 
+            // file extension?
+            for (; i < length; i++) {
+                switch (c) {
+                    case '/':
+                    case '\\':
+                        isPath = true;
+                        extensionIndex = -1;
+                        break;
+                    case '.':
+                        extensionIndex = i - 1;
+                        break;
+                }
+                c = name.charAt(i);
+            }
+
+            if (extensionIndex >= 0 && !EXECUTABLE_EXTENSIONS.contains(name.substring(extensionIndex).toLowerCase())) {
+                extensionIndex = -1;
+            }
+            
+            if (!executableOnly) {
+                if (isAbsolute) return name;
+            } else if (isPath) {
+                if (extensionIndex >= 0) return name;
+                
+                if (executableOnly) {
+                    return addExtension(name);
+                } else if (new File(name).exists()) {
+                    return name;
+                }
+
+                return null;
+            }
+
+            String[] paths = path.split(File.pathSeparator);
+            for (int p = 0; p < paths.length; p++) {
+                String currentPath = paths[p];
+                int currentPathLength = currentPath.length();
+                
+                if (currentPath == null || currentPathLength == 0) continue;
+                
+                if (currentPath.charAt(0) == '~' && 
+                    (currentPathLength == 1 || 
+                    (currentPathLength > 1 && (currentPath.charAt(1) == '/' || currentPath.charAt(1) == '\\')))) {
+                    String home = System.getenv("HOME");
+                    
+                    if (home != null) {
+                        currentPath = home + (currentPathLength == 1 ? "" : currentPath.substring(1));
+                    }
+                }
+                    
+                if (!currentPath.endsWith("/") && !currentPath.endsWith("\\")) {
+                    currentPath += "\\";
+                }
+                
+                String filename = currentPath + name;
+                if (Platform.IS_WINDOWS) filename = filename.replace('/', '\\');
+                
+                if (Platform.IS_WINDOWS && executableOnly && extensionIndex == -1) {
+                    String extendedFilename = addExtension(filename);
+                    
+                    if (extendedFilename != null) return extendedFilename;
+                    continue;
+                }
+
+                try {
+                    FileStat stat = posix.stat(filename);
+                    if (!executableOnly || (!stat.isDirectory() && stat.isExecutable())) return filename;
+                } catch (Throwable t) {}
+            }
+        }
+        
+        return null;
+    }
+    
+    public static String addExtension(String path) {
+        for (String extension : EXECUTABLE_EXTENSIONS) {
+            String newPath = path + extension;
+            
+            if (new File(newPath).exists()) return newPath;
+        }
+        
+        return null;
+    }
+}
diff --git a/src/main/java/jnr/posix/util/Java5ProcessMaker.java b/src/main/java/jnr/posix/util/Java5ProcessMaker.java
new file mode 100644
index 0000000..5f7f2c0
--- /dev/null
+++ b/src/main/java/jnr/posix/util/Java5ProcessMaker.java
@@ -0,0 +1,160 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: EPL 2.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Eclipse Public
+ * License Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+
+package jnr.posix.util;
+
+import jnr.posix.POSIXHandler;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+public class Java5ProcessMaker implements ProcessMaker {
+    private final ProcessBuilder builder;
+    private final POSIXHandler handler;
+
+    public Java5ProcessMaker(POSIXHandler handler, String... command) {
+        this.handler = handler;
+        builder = new ProcessBuilder(command);
+    }
+
+    public Java5ProcessMaker(POSIXHandler handler) {
+        this.handler = handler;
+        builder = new ProcessBuilder();
+    }
+
+    public List<String> command() {
+        return builder.command();
+    }
+
+    public ProcessMaker command(List<String> command) {
+        builder.command(command);
+        return this;
+    }
+
+    public ProcessMaker command(String... command) {
+        builder.command(command);
+        return this;
+    }
+
+    public File directory() {
+        return builder.directory();
+    }
+
+    public ProcessMaker directory(File dir) {
+        builder.directory(dir);
+        return this;
+    }
+
+    public Map<String, String> environment() {
+        return builder.environment();
+    }
+
+    public ProcessMaker environment(String[] envLines) {
+        envIntoProcessBuilder(builder, envLines);
+        return this;
+    }
+
+    public ProcessMaker inheritIO() {
+        handler.unimplementedError("inheritIO");
+        return this;
+    }
+
+    public Redirect redirectError() {
+        return Redirect.PIPE; // only option on Java 5/6
+    }
+
+    public ProcessMaker redirectError(File file) {
+        handler.unimplementedError("redirectError");
+        return this;
+    }
+
+    public ProcessMaker redirectError(Redirect destination) {
+        handler.unimplementedError("redirectError");
+        return this;
+    }
+
+    public boolean redirectErrorStream() {
+        return false;
+    }
+
+    public ProcessMaker redirectErrorStream(boolean redirectErrorStream) {
+        handler.unimplementedError("redirectErrorStream");
+        return this;
+    }
+
+    public Redirect redirectInput() {
+        return Redirect.PIPE; // only option on Java 5/6
+    }
+
+    public ProcessMaker redirectInput(File file) {
+        handler.unimplementedError("redirectInput");
+        return this;
+    }
+
+    public ProcessMaker redirectInput(Redirect source) {
+        handler.unimplementedError("redirectInput");
+        return this;
+    }
+
+    public Redirect redirectOutput() {
+        return Redirect.PIPE; // only option on Java 5/6
+    }
+
+    public ProcessMaker redirectOutput(File file) {
+        handler.unimplementedError("redirectOutput");
+        return this;
+    }
+
+    public ProcessMaker redirectOutput(Redirect destination) {
+        handler.unimplementedError("redirectOutput");
+        return this;
+    }
+
+    public Process start() throws IOException {
+        return builder.start();
+    }
+
+    private static void envIntoProcessBuilder(ProcessBuilder pb, String[] env) {
+        if (env == null) return;
+
+        pb.environment().clear();
+        for (String envLine : env) {
+            if (envLine.indexOf(0) != -1) {
+                envLine = envLine.replaceFirst("\u0000.*", "");
+            }
+
+            int index = envLine.indexOf('=');
+
+            if (index != -1) {
+                pb.environment().put(
+                        envLine.substring(0, index),
+                        envLine.substring(index + 1));
+            }
+        }
+    }
+}
diff --git a/src/main/java/jnr/posix/util/JavaCrypt.java b/src/main/java/jnr/posix/util/JavaCrypt.java
new file mode 100644
index 0000000..dc7f5c1
--- /dev/null
+++ b/src/main/java/jnr/posix/util/JavaCrypt.java
@@ -0,0 +1,232 @@
+package jnr.posix.util;
+
+public class JavaCrypt {
+    private static final int ITERATIONS = 16;
+    private static final int[] con_salt = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 0, 0, 0, 0, 0};
+    private static final boolean[] shifts2 = {false, false, true, true, true, true, true, true, false, true, true, true, true, true, true, false};
+    private static final int[][] skb = {{0, 16, 536870912, 536870928, 65536, 65552, 536936448, 536936464, 2048, 2064, 536872960, 536872976, 67584, 67600, 536938496, 536938512, 32, 48, 536870944, 536870960, 65568, 65584, 536936480, 536936496, 2080, 2096, 536872992, 536873008, 67616, 67632, 536938528, 536938544, 524288, 524304, 537395200, 537395216, 589824, 589840, 537460736, 537460752, 526336, 526352, 537397248, 537397264, 591872, 591888, 537462784, 537462800, 524320, 524336, 537395232, 537395248, 589856, 589872, 537460768, 537460784, 526368, 526384, 537397280, 537397296, 591904, 591920, 537462816, 537462832}, {0, 33554432, 8192, 33562624, 2097152, 35651584, 2105344, 35659776, 4, 33554436, 8196, 33562628, 2097156, 35651588, 2105348, 35659780, 1024, 33555456, 9216, 33563648, 2098176, 35652608, 2106368, 35660800, 1028, 33555460, 9220, 33563652, 2098180, 35652612, 2106372, 35660804, 268435456, 301989888, 268443648, 301998080, 270532608, 304087040, 270540800, 304095232, 268435460, 301989892, 268443652, 301998084, 270532612, 304087044, 270540804, 304095236, 268436480, 301990912, 268444672, 301999104, 270533632, 304088064, 270541824, 304096256, 268436484, 301990916, 268444676, 301999108, 270533636, 304088068, 270541828, 304096260}, {0, 1, 262144, 262145, 16777216, 16777217, 17039360, 17039361, 2, 3, 262146, 262147, 16777218, 16777219, 17039362, 17039363, 512, 513, 262656, 262657, 16777728, 16777729, 17039872, 17039873, 514, 515, 262658, 262659, 16777730, 16777731, 17039874, 17039875, 134217728, 134217729, 134479872, 134479873, 150994944, 150994945, 151257088, 151257089, 134217730, 134217731, 134479874, 134479875, 150994946, 150994947, 151257090, 151257091, 134218240, 134218241, 134480384, 134480385, 150995456, 150995457, 151257600, 151257601, 134218242, 134218243, 134480386, 134480387, 150995458, 150995459, 151257602, 151257603}, {0, 1048576, 256, 1048832, 8, 1048584, 264, 1048840, 4096, 1052672, 4352, 1052928, 4104, 1052680, 4360, 1052936, 67108864, 68157440, 67109120, 68157696, 67108872, 68157448, 67109128, 68157704, 67112960, 68161536, 67113216, 68161792, 67112968, 68161544, 67113224, 68161800, 131072, 1179648, 131328, 1179904, 131080, 1179656, 131336, 1179912, 135168, 1183744, 135424, 1184000, 135176, 1183752, 135432, 1184008, 67239936, 68288512, 67240192, 68288768, 67239944, 68288520, 67240200, 68288776, 67244032, 68292608, 67244288, 68292864, 67244040, 68292616, 67244296, 68292872}, {0, 268435456, 65536, 268500992, 4, 268435460, 65540, 268500996, 536870912, 805306368, 536936448, 805371904, 536870916, 805306372, 536936452, 805371908, 1048576, 269484032, 1114112, 269549568, 1048580, 269484036, 1114116, 269549572, 537919488, 806354944, 537985024, 806420480, 537919492, 806354948, 537985028, 806420484, 4096, 268439552, 69632, 268505088, 4100, 268439556, 69636, 268505092, 536875008, 805310464, 536940544, 805376000, 536875012, 805310468, 536940548, 805376004, 1052672, 269488128, 1118208, 269553664, 1052676, 269488132, 1118212, 269553668, 537923584, 806359040, 537989120, 806424576, 537923588, 806359044, 537989124, 806424580}, {0, 134217728, 8, 134217736, 1024, 134218752, 1032, 134218760, 131072, 134348800, 131080, 134348808, 132096, 134349824, 132104, 134349832, 1, 134217729, 9, 134217737, 1025, 134218753, 1033, 134218761, 131073, 134348801, 131081, 134348809, 132097, 134349825, 132105, 134349833, 33554432, 167772160, 33554440, 167772168, 33555456, 167773184, 33555464, 167773192, 33685504, 167903232, 33685512, 167903240, 33686528, 167904256, 33686536, 167904264, 33554433, 167772161, 33554441, 167772169, 33555457, 167773185, 33555465, 167773193, 33685505, 167903233, 33685513, 167903241, 33686529, 167904257, 33686537, 167904265}, {0, 256, 524288, 524544, 16777216, 16777472, 17301504, 17301760, 16, 272, 524304, 524560, 16777232, 16777488, 17301520, 17301776, 2097152, 2097408, 2621440, 2621696, 18874368, 18874624, 19398656, 19398912, 2097168, 2097424, 2621456, 2621712, 18874384, 18874640, 19398672, 19398928, 512, 768, 524800, 525056, 16777728, 16777984, 17302016, 17302272, 528, 784, 524816, 525072, 16777744, 16778000, 17302032, 17302288, 2097664, 2097920, 2621952, 2622208, 18874880, 18875136, 19399168, 19399424, 2097680, 2097936, 2621968, 2622224, 18874896, 18875152, 19399184, 19399440}, {0, 67108864, 262144, 67371008, 2, 67108866, 262146, 67371010, 8192, 67117056, 270336, 67379200, 8194, 67117058, 270338, 67379202, 32, 67108896, 262176, 67371040, 34, 67108898, 262178, 67371042, 8224, 67117088, 270368, 67379232, 8226, 67117090, 270370, 67379234, 2048, 67110912, 264192, 67373056, 2050, 67110914, 264194, 67373058, 10240, 67119104, 272384, 67381248, 10242, 67119106, 272386, 67381250, 2080, 67110944, 264224, 67373088, 2082, 67110946, 264226, 67373090, 10272, 67119136, 272416, 67381280, 10274, 67119138, 272418, 67381282}};
+    private static final int[][] SPtrans = {{8520192, 131072, -2139095040, -2138963456, 8388608, -2147352064, -2147352576, -2139095040, -2147352064, 8520192, 8519680, -2147483136, -2139094528, 8388608, 0, -2147352576, 131072, -2147483648, 8389120, 131584, -2138963456, 8519680, -2147483136, 8389120, -2147483648, 512, 131584, -2138963968, 512, -2139094528, -2138963968, 0, 0, -2138963456, 8389120, -2147352576, 8520192, 131072, -2147483136, 8389120, -2138963968, 512, 131584, -2139095040, -2147352064, -2147483648, -2139095040, 8519680, -2138963456, 131584, 8519680, -2139094528, 8388608, -2147483136, -2147352576, 0, 131072, 8388608, -2139094528, 8520192, -2147483648, -2138963968, 512, -2147352064}, {268705796, 0, 270336, 268697600, 268435460, 8196, 268443648, 270336, 8192, 268697604, 4, 268443648, 262148, 268705792, 268697600, 4, 262144, 268443652, 268697604, 8192, 270340, 268435456, 0, 262148, 268443652, 270340, 268705792, 268435460, 268435456, 262144, 8196, 268705796, 262148, 268705792, 268443648, 270340, 268705796, 262148, 268435460, 0, 268435456, 8196, 262144, 268697604, 8192, 268435456, 270340, 268443652, 268705792, 8192, 0, 268435460, 4, 268705796, 270336, 268697600, 268697604, 262144, 8196, 268443648, 268443652, 4, 268697600, 270336}, {1090519040, 16842816, 64, 1090519104, 1073807360, 16777216, 1090519104, 65600, 16777280, 65536, 16842752, 1073741824, 1090584640, 1073741888, 1073741824, 1090584576, 0, 1073807360, 16842816, 64, 1073741888, 1090584640, 65536, 1090519040, 1090584576, 16777280, 1073807424, 16842752, 65600, 0, 16777216, 1073807424, 16842816, 64, 1073741824, 65536, 1073741888, 1073807360, 16842752, 1090519104, 0, 16842816, 65600, 1090584576, 1073807360, 16777216, 1090584640, 1073741824, 1073807424, 1090519040, 16777216, 1090584640, 65536, 16777280, 1090519104, 65600, 16777280, 0, 1090584576, 1073741888, 1090519040, 1073807424, 64, 16842752}, {1049602, 67109888, 2, 68158466, 0, 68157440, 67109890, 1048578, 68158464, 67108866, 67108864, 1026, 67108866, 1049602, 1048576, 67108864, 68157442, 1049600, 1024, 2, 1049600, 67109890, 68157440, 1024, 1026, 0, 1048578, 68158464, 67109888, 68157442, 68158466, 1048576, 68157442, 1026, 1048576, 67108866, 1049600, 67109888, 2, 68157440, 67109890, 0, 1024, 1048578, 0, 68157442, 68158464, 1024, 67108864, 68158466, 1049602, 1048576, 68158466, 2, 67109888, 1049602, 1048578, 1049600, 68157440, 67109890, 1026, 67108864, 67108866, 68158464}, {33554432, 16384, 256, 33571080, 33570824, 33554688, 16648, 33570816, 16384, 8, 33554440, 16640, 33554696, 33570824, 33571072, 0, 16640, 33554432, 16392, 264, 33554688, 16648, 0, 33554440, 8, 33554696, 33571080, 16392, 33570816, 256, 264, 33571072, 33571072, 33554696, 16392, 33570816, 16384, 8, 33554440, 33554688, 33554432, 16640, 33571080, 0, 16648, 33554432, 256, 16392, 33554696, 256, 0, 33571080, 33570824, 33571072, 264, 16384, 16640, 33570824, 33554688, 264, 8, 16648, 33570816, 33554440}, {536870928, 524304, 0, 537397248, 524304, 2048, 536872976, 524288, 2064, 537397264, 526336, 536870912, 536872960, 536870928, 537395200, 526352, 524288, 536872976, 537395216, 0, 2048, 16, 537397248, 537395216, 537397264, 537395200, 536870912, 2064, 16, 526336, 526352, 536872960, 2064, 536870912, 536872960, 526352, 537397248, 524304, 0, 536872960, 536870912, 2048, 537395216, 524288, 524304, 537397264, 526336, 16, 537397264, 526336, 524288, 536872976, 536870928, 537395200, 526352, 0, 2048, 536870928, 536872976, 537397248, 537395200, 2064, 16, 537395216}, {4096, 128, 4194432, 4194305, 4198529, 4097, 4224, 0, 4194304, 4194433, 129, 4198400, 1, 4198528, 4198400, 129, 4194433, 4096, 4097, 4198529, 0, 4194432, 4194305, 4224, 4198401, 4225, 4198528, 1, 4225, 4198401, 128, 4194304, 4225, 4198400, 4198401, 129, 4096, 128, 4194304, 4198401, 4194433, 4225, 4224, 0, 128, 4194305, 1, 4194432, 0, 4194433, 4194432, 4224, 129, 4096, 4198529, 4194304, 4198528, 1, 4097, 4198529, 4194305, 4198528, 4198400, 4097}, {136314912, 136347648, 32800, 0, 134250496, 2097184, 136314880, 136347680, 32, 134217728, 2129920, 32800, 2129952, 134250528, 134217760, 136314880, 32768, 2129952, 2097184, 134250496, 136347680, 134217760, 0, 2129920, 134217728, 2097152, 134250528, 136314912, 2097152, 32768, 136347648, 32, 2097152, 32768, 134217760, 136347680, 32800, 134217728, 0, 2129920, 136314912, 134250528, 134250496, 2097184, 136347648, 32, 2097184, 134250496, 136347680, 2097152, 136314880, 134217760, 2129920, 32800, 134250528, 136314880, 32, 136347648, 2129952, 0, 134217728, 136314912, 32768, 2129952}};
+    private static final int[] cov_2char = {46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122};
+
+    private static final int byteToUnsigned(byte b) {
+        return b & 255;
+    }
+
+    private static int fourBytesToInt(byte[] b, int offset) {
+        int value;
+        value = byteToUnsigned(b[offset++]);
+        value |= (byteToUnsigned(b[offset++]) << 8);
+        value |= (byteToUnsigned(b[offset++]) << 16);
+        value |= (byteToUnsigned(b[offset++]) << 24);
+        return value;
+    }
+
+    private static final void intToFourBytes(int iValue, byte[] b, int offset) {
+        b[offset++] = (byte) ((iValue) & 255);
+        b[offset++] = (byte) ((iValue >>> 8) & 255);
+        b[offset++] = (byte) ((iValue >>> 16) & 255);
+        b[offset++] = (byte) ((iValue >>> 24) & 255);
+    }
+
+    private static final void PERM_OP(int a, int b, int n, int m, int[] results) {
+        int t;
+
+        t = ((a >>> n) ^ b) & m;
+        a ^= t << n;
+        b ^= t;
+
+        results[0] = a;
+        results[1] = b;
+    }
+
+    private static final int HPERM_OP(int a, int n, int m) {
+        int t;
+
+        t = ((a << (16 - n)) ^ a) & m;
+        a = a ^ t ^ (t >>> (16 - n));
+
+        return a;
+    }
+
+    private static int[] des_set_key(byte[] key) {
+        int[] schedule = new int[ITERATIONS * 2];
+
+        int c = fourBytesToInt(key, 0);
+        int d = fourBytesToInt(key, 4);
+
+        int[] results = new int[2];
+
+        PERM_OP(d, c, 4, 252645135, results);
+        d = results[0];
+        c = results[1];
+
+        c = HPERM_OP(c, -2, -859045888);
+        d = HPERM_OP(d, -2, -859045888);
+
+        PERM_OP(d, c, 1, 1431655765, results);
+        d = results[0];
+        c = results[1];
+
+        PERM_OP(c, d, 8, 16711935, results);
+        c = results[0];
+        d = results[1];
+
+        PERM_OP(d, c, 1, 1431655765, results);
+        d = results[0];
+        c = results[1];
+
+        d = (((d & 255) << 16) | (d & 65280) | ((d & 16711680) >>> 16) | ((c & -268435456) >>> 4));
+        c &= 268435455;
+
+        int s;
+        int t;
+        int j = 0;
+
+        for (int i = 0; i < ITERATIONS; i++) {
+            if (shifts2[i]) {
+                c = (c >>> 2) | (c << 26);
+                d = (d >>> 2) | (d << 26);
+            } else {
+                c = (c >>> 1) | (c << 27);
+                d = (d >>> 1) | (d << 27);
+            }
+
+            c &= 268435455;
+            d &= 268435455;
+
+            s = skb[0][(c) & 63] | skb[1][((c >>> 6) & 3) | ((c >>> 7) & 60)] | skb[2][((c >>> 13) & 15) | ((c >>> 14) & 48)] | skb[3][((c >>> 20) & 1) | ((c >>> 21) & 6) | ((c >>> 22) & 56)];
+
+            t = skb[4][(d) & 63] | skb[5][((d >>> 7) & 3) | ((d >>> 8) & 60)] | skb[6][(d >>> 15) & 63] | skb[7][((d >>> 21) & 15) | ((d >>> 22) & 48)];
+
+            schedule[j++] = ((t << 16) | (s & 65535)) & -1;
+            s = ((s >>> 16) | (t & -65536));
+
+            s = (s << 4) | (s >>> 28);
+            schedule[j++] = s & -1;
+        }
+        return schedule;
+    }
+
+    private static final int D_ENCRYPT(int L, int R, int S, int E0, int E1, int[] s) {
+        int t;
+        int u;
+        int v;
+
+        v = R ^ (R >>> 16);
+        u = v & E0;
+        v = v & E1;
+        u = (u ^ (u << 16)) ^ R ^ s[S];
+        t = (v ^ (v << 16)) ^ R ^ s[S + 1];
+        t = (t >>> 4) | (t << 28);
+
+        L ^= SPtrans[1][(t) & 63] | SPtrans[3][(t >>> 8) & 63] | SPtrans[5][(t >>> 16) & 63] | SPtrans[7][(t >>> 24) & 63] | SPtrans[0][(u) & 63] | SPtrans[2][(u >>> 8) & 63] | SPtrans[4][(u >>> 16) & 63] | SPtrans[6][(u >>> 24) & 63];
+
+        return L;
+    }
+
+    private static final int[] body(int[] schedule, int Eswap0, int Eswap1) {
+        int left = 0;
+        int right = 0;
+        int t = 0;
+
+        for (int j = 0; j < 25; j++) {
+            for (int i = 0; i < ITERATIONS * 2; i += 4) {
+                left = D_ENCRYPT(left, right, i, Eswap0, Eswap1, schedule);
+                right = D_ENCRYPT(right, left, i + 2, Eswap0, Eswap1, schedule);
+            }
+            t = left;
+            left = right;
+            right = t;
+        }
+
+        t = right;
+
+        right = (left >>> 1) | (left << 31);
+        left = (t >>> 1) | (t << 31);
+
+        left &= -1;
+        right &= -1;
+
+        int[] results = new int[2];
+
+        PERM_OP(right, left, 1, 1431655765, results);
+        right = results[0];
+        left = results[1];
+
+        PERM_OP(left, right, 8, 16711935, results);
+        left = results[0];
+        right = results[1];
+
+        PERM_OP(right, left, 2, 858993459, results);
+        right = results[0];
+        left = results[1];
+
+        PERM_OP(left, right, 16, 65535, results);
+        left = results[0];
+        right = results[1];
+
+        PERM_OP(right, left, 4, 252645135, results);
+        right = results[0];
+        left = results[1];
+
+        int[] out = new int[2];
+
+        out[0] = left;
+        out[1] = right;
+
+        return out;
+    }
+
+    public static final CharSequence crypt(CharSequence original, CharSequence salt) {
+        char[] buffer = new char[]{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '};
+
+        char charZero = salt.charAt(0);
+        char charOne = salt.charAt(1);
+
+        buffer[0] = charZero;
+        buffer[1] = charOne;
+
+        int Eswap0 = con_salt[charZero & 127];
+        int Eswap1 = con_salt[charOne & 127] << 4;
+
+        byte[] key = new byte[8];
+
+        for (int i = 0; i < key.length; i++) {
+            key[i] = (byte) 0;
+        }
+
+        for (int i = 0; i < key.length && i < original.length(); i++) {
+            int iChar = original.charAt(i) & 255;
+
+            key[i] = (byte) (iChar << 1);
+        }
+
+        int[] schedule = des_set_key(key);
+        int[] out = body(schedule, Eswap0, Eswap1);
+
+        byte[] b = new byte[9];
+
+        intToFourBytes(out[0], b, 0);
+        intToFourBytes(out[1], b, 4);
+        b[8] = 0;
+
+        for (int i = 2, y = 0, u = 128; i < 13; i++) {
+            for (int j = 0, c = 0; j < 6; j++) {
+                c <<= 1;
+
+                if (((int) b[y] & u) != 0) {
+                    c |= 1;
+                }
+
+                u >>>= 1;
+
+                if (u == 0) {
+                    y++;
+                    u = 128;
+                }
+                buffer[i] = (char) cov_2char[c];
+            }
+        }
+        return new String(buffer);
+    }
+}
diff --git a/src/main/java/jnr/posix/util/MethodName.java b/src/main/java/jnr/posix/util/MethodName.java
new file mode 100644
index 0000000..f993af6
--- /dev/null
+++ b/src/main/java/jnr/posix/util/MethodName.java
@@ -0,0 +1,25 @@
+package jnr.posix.util;
+
+public class MethodName {
+    private static final int CLIENT_CODE_STACK_INDEX;
+
+    static {
+        // Finds out the index of "this code" in the returned stack trace - funny but it differs in JDK 1.5 and 1.6
+        int i = 0;
+        for (StackTraceElement ste : Thread.currentThread().getStackTrace()) {
+            i++;
+            if (ste.getClassName().equals(MethodName.class.getName())) {
+                break;
+            }
+        }
+        CLIENT_CODE_STACK_INDEX = i;
+    }
+
+    public static String getMethodName() {
+        return Thread.currentThread().getStackTrace()[CLIENT_CODE_STACK_INDEX].getMethodName();
+    }
+
+    public static String getCallerMethodName() {
+        return Thread.currentThread().getStackTrace()[CLIENT_CODE_STACK_INDEX + 1].getMethodName();
+    }
+}
diff --git a/src/main/java/jnr/posix/util/Platform.java b/src/main/java/jnr/posix/util/Platform.java
new file mode 100644
index 0000000..e961a23
--- /dev/null
+++ b/src/main/java/jnr/posix/util/Platform.java
@@ -0,0 +1,128 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: EPL 2.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Eclipse Public
+ * License Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * 
+ *  
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+/**
+ * $Id: $
+ */
+package jnr.posix.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Platform {
+    public static final String OS_NAME = System.getProperty("os.name");
+    public static final String OS_NAME_LC = OS_NAME.toLowerCase();
+
+    // Generic Windows designation
+    private static final String WINDOWS = "windows";
+    // For Windows 95, 98... Would these platforms actually work?
+    private static final String WINDOWS_9X = "windows 9";
+    // TODO: Windows ME?
+    private static final String WINDOWS_NT = "nt";
+    private static final String WINDOWS_20X = "windows 2";
+    private static final String WINDOWS_XP = "windows xp";
+    private static final String WINDOWS_SERVER = "server";
+    private static final String WINDOWS_VISTA = "vista";
+    private static final String WINDOWS_7 = "windows 7";
+    private static final String MAC_OS = "mac os";
+    private static final String DARWIN = "darwin";
+    private static final String FREEBSD = "freebsd";
+    private static final String DRAGONFLY = "dragonfly";
+    private static final String OPENBSD = "openbsd";
+    private static final String LINUX = "linux";
+    private static final String SOLARIS = "sunos";
+
+    // TODO: investigate supported platforms for OpenJDK7?
+
+    public static final boolean IS_WINDOWS = OS_NAME_LC.indexOf(WINDOWS) != -1;
+    public static final boolean IS_WINDOWS_9X = OS_NAME_LC.indexOf(WINDOWS_9X) > -1;
+    public static final boolean IS_WINDOWS_NT = IS_WINDOWS && OS_NAME_LC.indexOf(WINDOWS_NT) > -1;
+    public static final boolean IS_WINDOWS_20X = OS_NAME_LC.indexOf(WINDOWS_20X) > -1;
+    public static final boolean IS_WINDOWS_XP = OS_NAME_LC.indexOf(WINDOWS_XP) > -1;
+    public static final boolean IS_WINDOWS_VISTA = IS_WINDOWS && OS_NAME_LC.indexOf(WINDOWS_VISTA) > -1;
+    public static final boolean IS_WINDOWS_SERVER = IS_WINDOWS && OS_NAME_LC.indexOf(WINDOWS_SERVER) > -1;
+    public static final boolean IS_WINDOWS_7 = IS_WINDOWS && OS_NAME_LC.indexOf(WINDOWS_7) > -1;
+    public static final boolean IS_MAC = OS_NAME_LC.startsWith(MAC_OS) || OS_NAME_LC.startsWith(DARWIN);
+    public static final boolean IS_FREEBSD = OS_NAME_LC.startsWith(FREEBSD);
+    public static final boolean IS_DRAGONFLY = OS_NAME_LC.startsWith(DRAGONFLY);
+    public static final boolean IS_OPENBSD = OS_NAME_LC.startsWith(OPENBSD);
+    public static final boolean IS_LINUX = OS_NAME_LC.startsWith(LINUX);   
+    public static final boolean IS_SOLARIS = OS_NAME_LC.startsWith(SOLARIS);
+    public static final boolean IS_BSD = IS_MAC || IS_FREEBSD || IS_OPENBSD || IS_DRAGONFLY;
+    
+    public static final String envCommand() {
+        if (IS_WINDOWS) {
+            if (IS_WINDOWS_9X) {
+                return "command.com /c set";
+            } else if (IS_WINDOWS_NT || IS_WINDOWS_20X || IS_WINDOWS_XP ||
+                       IS_WINDOWS_SERVER || IS_WINDOWS_VISTA || IS_WINDOWS_7) {
+                return "cmd.exe /c set";
+            }
+        }
+        return "env";
+    }
+
+    public static final boolean IS_32_BIT = "32".equals(getProperty("sun.arch.data.model", "32"));
+    public static final boolean IS_64_BIT = "64".equals(getProperty("sun.arch.data.model", "64"));
+
+    public static final String ARCH;
+    static {
+        String arch = System.getProperty("os.arch");
+        if (arch.equals("amd64")) arch = "x86_64";
+        ARCH = arch;
+    }
+    
+    public static final Map<String, String> OS_NAMES = new HashMap<String, String>();
+    static {
+        OS_NAMES.put("Mac OS X", DARWIN);
+        OS_NAMES.put("Darwin", DARWIN);
+        OS_NAMES.put("Linux", LINUX);
+    }
+
+    public static String getOSName()  {
+        String theOSName = OS_NAMES.get(OS_NAME);
+        
+        return theOSName == null ? OS_NAME : theOSName;
+    }
+    
+    /**
+     * An extension over <code>System.getProperty</code> method.
+     * Handles security restrictions, and returns the default
+     * value if the access to the property is restricted.
+     * @param property The system property name.
+     * @param defValue The default value.
+     * @return The value of the system property,
+     *         or the default value.
+     */
+    public static String getProperty(String property, String defValue) {
+        try {
+            return System.getProperty(property, defValue);
+        } catch (SecurityException se) {
+            return defValue;
+        }
+    }
+}
diff --git a/src/main/java/jnr/posix/util/ProcessMaker.java b/src/main/java/jnr/posix/util/ProcessMaker.java
new file mode 100644
index 0000000..2dd5aac
--- /dev/null
+++ b/src/main/java/jnr/posix/util/ProcessMaker.java
@@ -0,0 +1,103 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: EPL 2.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Eclipse Public
+ * License Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+
+package jnr.posix.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Abstract interface for building a process on various JVM versions.
+ */
+public interface ProcessMaker {
+    public static class Redirect {
+        public static final Redirect INHERIT = new Redirect(Type.INHERIT);
+        public static final Redirect PIPE = new Redirect(Type.PIPE);
+
+        private enum Type {
+            APPEND,
+            INHERIT,
+            PIPE,
+            READ,
+            WRITE
+        }
+
+        private final Type type;
+        private final File file;
+
+        private Redirect(Type type) {
+            this(type, null);
+        }
+
+        private Redirect(Type type, File file) {
+            this.type = type;
+            this.file = file;
+        }
+
+        public static Redirect appendTo(File file) {
+            return new Redirect(Type.APPEND, file);
+        }
+
+        public static Redirect from(File file) {
+            return new Redirect(Type.READ, file);
+        }
+
+        public static Redirect to(File file) {
+            return new Redirect(Type.WRITE, file);
+        }
+
+        public File file() {
+            return file;
+        }
+
+        public Type type() {
+            return type;
+        }
+    }
+
+    public List<String> command();
+    public ProcessMaker command(List<String> command);
+    public ProcessMaker command(String... command);
+    public File directory();
+    public ProcessMaker directory(File dir);
+    public Map<String, String> environment();
+    public ProcessMaker environment(String[] envLines);
+    public ProcessMaker inheritIO();
+    public Redirect redirectError();
+    public ProcessMaker redirectError(File file);
+    public ProcessMaker redirectError(Redirect destination);
+    public boolean redirectErrorStream();
+    public ProcessMaker redirectErrorStream(boolean redirectErrorStream);
+    public Redirect redirectInput();
+    public ProcessMaker redirectInput(File file);
+    public ProcessMaker redirectInput(Redirect source);
+    public Redirect redirectOutput();
+    public ProcessMaker redirectOutput(File file);
+    public ProcessMaker redirectOutput(Redirect destination);
+    public Process start() throws IOException;
+}
diff --git a/src/main/java/jnr/posix/util/SunMiscSignal.java b/src/main/java/jnr/posix/util/SunMiscSignal.java
new file mode 100644
index 0000000..164a52e
--- /dev/null
+++ b/src/main/java/jnr/posix/util/SunMiscSignal.java
@@ -0,0 +1,36 @@
+package jnr.posix.util;
+
+import jnr.posix.SignalHandler;
+import sun.misc.Signal;
+
+public class SunMiscSignal {
+    public static SignalHandler signal(jnr.constants.platform.Signal sig, final SignalHandler handler) {
+        Signal s = new Signal(sig.name().substring("SIG".length()));
+
+        sun.misc.SignalHandler oldHandler = Signal.handle(s, new SunMiscSignalHandler(handler));
+
+        if (oldHandler instanceof SunMiscSignalHandler) {
+            return ((SunMiscSignalHandler)oldHandler).handler;
+        } else if (oldHandler != null) {
+            return new SignalHandler() {
+                @Override
+                public void handle(int signal) {
+                    oldHandler.handle(s);
+                }
+            };
+        } else {
+            return null;
+        }
+    }
+
+    private static class SunMiscSignalHandler implements sun.misc.SignalHandler {
+        final SignalHandler handler;
+        public SunMiscSignalHandler(SignalHandler handler) {
+            this.handler = handler;
+        }
+
+        public void handle(Signal signal) {
+            handler.handle(signal.getNumber());
+        }
+    }
+}
diff --git a/src/main/java/jnr/posix/util/WindowsHelpers.java b/src/main/java/jnr/posix/util/WindowsHelpers.java
new file mode 100644
index 0000000..705a111
--- /dev/null
+++ b/src/main/java/jnr/posix/util/WindowsHelpers.java
@@ -0,0 +1,422 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package jnr.posix.util;
+
+import jnr.ffi.*;
+
+import java.io.File;
+import java.io.UnsupportedEncodingException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.StringTokenizer;
+import jnr.posix.POSIX;
+
+/**
+ *
+ * @author enebo
+ */
+public class WindowsHelpers {
+    static final jnr.ffi.Runtime runtime = jnr.ffi.Runtime.getSystemRuntime();
+    static final int WORDSIZE = jnr.ffi.Runtime.getSystemRuntime().addressSize();
+    
+    public static byte[] toWPath(String path) {
+        return toWString(path);
+    }
+
+    public static byte[] toWString(String string) {
+        if (string == null) return null;
+        
+        string += (char) 0;
+
+        try {
+            return string.getBytes("UTF-16LE");
+        } catch (UnsupportedEncodingException e) {
+            return null; // JVM mandates this encoding. Not reached
+        }
+    }
+    
+    // FIXME: This does not work and I am unsure if it is because I am violating something
+    // CreateProcess requires OR because there are weird requirements in how env needs to be
+    // setup for CreateProcess (e.g. =C:=C:/ vars).
+    public static Pointer createWideEnv(String[] envp) {
+        if (envp == null) return null;
+        byte[] marker = {0};        
+        int envLength = envp.length;
+
+        // Allocate pointer for env pointer entries plus last \0\0 marker
+        Pointer result = Memory.allocateDirect(runtime, WORDSIZE * (envLength + 1));
+        
+        for (int i = 0; i < envLength; i++) {
+            byte[] bytes = toWString(envp[i]);
+            Pointer envElement = Memory.allocateDirect(runtime, bytes.length + 1);
+            envElement.put(0, bytes, 0, bytes.length);
+            envElement.put(bytes.length, marker, 0, marker.length);
+            result.putPointer(i * WORDSIZE, envElement);
+        }
+
+        Pointer nullMarker = Memory.allocateDirect(runtime, marker.length);
+        nullMarker.put(0, marker, 0, marker.length);
+        result.putPointer(WORDSIZE * envLength, nullMarker);
+
+        
+        return result;
+    }
+    // Windows cmd strings have various escaping:
+    // 1. <>|^ can all be escaped with ^ (e.g. ^<)
+    // 2. \s\t must be quoted if not already
+    // 3. Any arguments with double quotes must be escaped with a double 
+    //    quote around whole cmd    
+    private static void joinSingleArgv(StringBuilder buffer, String arg, 
+            boolean quote, boolean escape) {
+        int backslashCount = 0;
+        int start = 0;
+        
+        if (quote) buffer.append('"');
+        
+        for (int i = 0; i < arg.length(); i++) {
+            char c = arg.charAt(i);
+            switch(c) {
+                case '\\':
+                    backslashCount++;
+                    break;
+                case '"': {
+                    buffer.append(arg.substring(start, i));
+                    for (int j = 0; j < backslashCount + 1; j++) {
+                        buffer.append('\\');
+                    }
+                    backslashCount = 0;
+                    start = i;
+                }
+                case '<': case '>': case '|': case '^': {
+                    if (escape && !quote) {
+                        buffer.append(arg.substring(start, i));
+                        buffer.append('^');
+                        start = i;
+                        break;
+                    }
+                }
+                default: {
+                    backslashCount = 0;
+                    break;
+                }
+            }
+        }
+        buffer.append(arg.substring(start));
+        
+        if (quote) buffer.append('"');
+    }
+    
+    public static String joinArgv(String command, String[] argv, boolean escape) {
+        StringBuilder buffer = new StringBuilder();
+
+        if (command != null) {
+            buffer.append(command);
+            buffer.append(' ');
+        }
+
+        int last_index = argv.length - 1;
+        for (int i = 0; i <= last_index; i++) {
+            joinSingleArgv(buffer, argv[i], quotable(argv[i]), escape);
+            if (i != last_index) buffer.append(' '); // Add space between arguments
+        }
+        
+        return buffer.toString();
+    }    
+    
+    
+    public static boolean quotable(String value) {
+        if (value == null) return false;
+        StringTokenizer toker = new StringTokenizer(value, " \t\"'");
+        toker.nextToken(); // We know a string with no delimeters will return self
+        return toker.hasMoreTokens();
+    }
+    
+    public static boolean isBatch(String value) {
+        if (value == null) return false;
+        int length = value.length();
+        
+        if (length < 5) return false;
+        
+        String end = value.substring(length - 4);
+        
+        return end.equalsIgnoreCase(".bat") || end.equalsIgnoreCase(".cmd");
+    }
+    
+    public static String[] processCommandLine(POSIX posix, String command, 
+            String program, String path) {
+        String shell = null;
+        
+        if (program != null) {
+            String fullPath = Finder.findFileInPath(posix, program, path);
+            
+            shell = fullPath == null ? program : fullPath.replace('/', '\\');
+        } else {
+            // Strip off leading whitespace
+            command = command.substring(firstNonWhitespaceIndex(command));
+            
+            // FIXME: Ruby first looks for RUBYSHELL, but this only applies for
+            // JRuby (I doubt Jython wants to honor that env).  We need a generic
+            // hook for other envs to look for?
+            shell = System.getenv("COMSPEC");
+            boolean notHandledYet = true;
+            if (shell != null) {
+                boolean commandDotCom = isCommandDotCom(shell);
+                if (hasBuiltinSpecialNeeds(command) || isInternalCommand(command, commandDotCom)) {
+                    String quote = commandDotCom ? "\"" : "";
+                    command = shell + " /c " + quote + command + quote;
+                    notHandledYet = false;
+                }
+            }
+            
+            if (notHandledYet) {
+                char firstChar = command.charAt(0);
+                char quote = firstChar == '"' ? firstChar : (firstChar == '\'' ? firstChar : (char) 0);
+                int commandLength = command.length();
+
+                int i = quote == 0 ? 0 : 1;
+                
+                for(;; i++) {
+                    if (i == commandLength) {
+                        shell = command;
+                        break;
+                    }
+                    
+                    char c = command.charAt(i);
+                    
+                    if (c == quote) {
+                        shell = command.substring(1, i);
+                        break;
+                    }
+                    if (quote != 0) continue;
+                    
+                    if (Character.isSpaceChar(c) || isFunnyChar(c)) {
+                        shell = command.substring(0, i);
+                        break;
+                    }
+                }
+                shell = Finder.findFileInPath(posix, shell, path);
+                
+                if (shell == null) {
+                    shell = command.substring(0, i);
+                } else {
+                    if (!shell.contains(" ")) quote = 0;
+                    
+                    shell = shell.replace('/', '\\');
+                }
+            }                
+        }
+        
+        return new String[] { command, shell };
+    }
+
+    public static String[] processCommandArgs(POSIX posix, String program, 
+            String[] argv, String path) {
+           if (program == null || program.length() == 0) program = argv[0];
+        
+        boolean addSlashC = false;
+        boolean isNotBuiltin = false;
+        boolean notHandledYet = true;
+        String shell = System.getenv("COMSPEC");
+        String command = null;
+        if (shell != null) {
+            boolean commandDotCom = isCommandDotCom(shell);
+            if (isInternalCommand(program, commandDotCom)) {
+                isNotBuiltin = !commandDotCom;
+                program = shell;
+                addSlashC = true;
+                notHandledYet = false;
+            }
+        }
+        if (notHandledYet) {
+            command = Finder.findFileInPath(posix, program, path);
+            if (command != null) {
+                program = command.replace('/', '\\');
+            } else if (program.contains("/")) {
+                command = program.replace('/', '\\');
+                program = command;
+            }
+        }
+        
+        if (addSlashC || isBatch(program)) {
+            if (addSlashC) {
+                command = program + " /c ";
+            } else {
+                String[] newArgv = new String[argv.length - 1];
+                System.arraycopy(argv, 1, newArgv, 0, argv.length - 1);
+                argv = newArgv;
+            }
+
+            if (argv.length > 0) {
+                command = WindowsHelpers.joinArgv(command, argv, isNotBuiltin);
+            }
+            program = addSlashC ? shell : null;
+        } else {
+            command = WindowsHelpers.joinArgv(null, argv, false);
+        }
+        
+        return new String[] { command, program };
+    }
+    
+    private static boolean isFunnyChar(char c) {
+        return c == '<' || c == '>' || c == '|' || c == '*' || c == '?' ||
+                c == '"';
+    }
+
+    private static boolean hasBuiltinSpecialNeeds(String value) {
+        int length = value.length();
+        char quote = '\0';
+        
+        for (int i = 0; i < length; i++) {
+            char c = value.charAt(i);
+            switch (c) {
+                case '\'': case '\"':
+                    if (quote == '\0') {
+                        quote = c;
+                    } else if (quote == c) {
+                        quote = '\0';
+                    }
+                    break;
+                case '>': case '<': case '|': case '\n':
+                    if (quote != '\0') return true;
+                    break;
+                case '%':  // %FOO% check
+                    if (i + 1 < length) {
+                        i += 1;
+                        char c2 = value.charAt(i);
+                        if (c2 != ' ' && !Character.isLetter(c2)) break;
+                        for (int j = i; j < length; j++) {
+                            c2 = value.charAt(j);
+                            if (c2 != ' ' && !Character.isLetterOrDigit(c2)) break;
+                        }
+                        if (c2 == '%') return true;
+                    }
+                    break;
+            }
+	}
+        return false;
+    }
+    
+    private static int firstNonWhitespaceIndex(String value) {
+        int length = value.length();
+        int i = 0;
+        for (; i < length && Character.isSpaceChar(value.charAt(i)); i++) {}
+        return i;
+    }
+    
+    public static String escapePath(String path) {
+        StringBuilder buf = new StringBuilder();
+        
+        for (int i = 0; i < path.length(); i++) {
+            char c = path.charAt(i);
+            
+            buf.append(c);
+            if (c == '\\') buf.append(c);
+        }
+        return buf.toString() + "\\\\";
+    }    
+    
+    private final static String COMMAND_DOT_COM = "command.com";
+    private final static int CDC_LENGTH = COMMAND_DOT_COM.length();
+    private enum InternalType { SHELL, COMMAND, BOTH };
+    private static Map<String, InternalType> INTERNAL_COMMANDS = new HashMap<String, InternalType>() {{
+        put("assoc", InternalType.COMMAND);
+        put("break", InternalType.BOTH);
+        put("call", InternalType.BOTH);
+        put("cd", InternalType.BOTH);
+        put("chcp", InternalType.SHELL);
+        put("chdir", InternalType.BOTH);
+        put("cls", InternalType.BOTH);
+        put("color", InternalType.COMMAND);
+        put("copy", InternalType.BOTH);
+        put("ctty", InternalType.SHELL);
+        put("date", InternalType.BOTH);
+        put("del", InternalType.BOTH);
+        put("dir", InternalType.BOTH);
+        put("echo", InternalType.BOTH);
+        put("endlocal", InternalType.COMMAND);
+        put("erase", InternalType.BOTH);
+        put("exit", InternalType.BOTH);
+        put("for", InternalType.BOTH);
+        put("ftype", InternalType.COMMAND);
+        put("goto", InternalType.BOTH);
+        put("if", InternalType.BOTH);
+        put("lfnfor", InternalType.SHELL);
+        put("lh", InternalType.SHELL);
+        put("lock", InternalType.SHELL);
+        put("md", InternalType.BOTH);
+        put("mkdir", InternalType.BOTH);
+        put("move", InternalType.COMMAND);
+        put("path", InternalType.BOTH);
+        put("pause", InternalType.BOTH);
+        put("popd", InternalType.COMMAND);
+        put("prompt", InternalType.BOTH);
+        put("pushd", InternalType.COMMAND);
+        put("rd", InternalType.BOTH);
+        put("rem", InternalType.BOTH);
+        put("ren", InternalType.BOTH);
+        put("rename", InternalType.BOTH);
+        put("rmdir", InternalType.BOTH);
+        put("set", InternalType.BOTH);
+        put("setlocal", InternalType.COMMAND);
+        put("shift", InternalType.BOTH);
+        put("start", InternalType.COMMAND);
+        put("time", InternalType.BOTH);
+        put("title", InternalType.COMMAND);
+        put("truename", InternalType.SHELL);
+        put("type", InternalType.BOTH);
+        put("unlock", InternalType.SHELL);
+        put("ver", InternalType.BOTH);
+        put("verify", InternalType.BOTH);
+        put("vol", InternalType.BOTH);
+    }};
+    
+    private static boolean isDirectorySeparator(char value) {
+        return value == '/' || value == '\\';
+    }    
+    private static boolean isCommandDotCom(String command) {
+        int length = command.length();
+        int i = length - CDC_LENGTH;
+        
+        return i == 0 || i > 0 && isDirectorySeparator(command.charAt(i - 1)) &&
+                command.regionMatches(true, i, COMMAND_DOT_COM, 0, CDC_LENGTH);
+    }
+    
+    private static boolean isInternalCommand(String command, boolean hasCommandDotCom) {
+        assert command != null && !Character.isSpaceChar(command.charAt(0)) : "Spaces should have been stripped off already";
+        
+        int length = command.length();
+        
+        StringBuilder buf = new StringBuilder();
+        int i = 0;
+        char c = 0;
+        for (; i < length; i++) {
+            c = command.charAt(i);
+            if (!Character.isLetter(c)) break;
+            buf.append(Character.toLowerCase(c));
+        }
+        
+        if (i < length) {
+            if (c == '.' && i + 1 < length) i++;
+
+            switch (command.charAt(i)) {
+                case '<': case '>': case '|':
+                    return true;
+                case '\0': case ' ': case '\t': case '\n':
+                    break;
+                default:
+                    return false;
+            }
+        }
+
+        InternalType kindOf = INTERNAL_COMMANDS.get(buf.toString());
+        return kindOf == InternalType.BOTH || 
+                (hasCommandDotCom ? kindOf == InternalType.COMMAND : kindOf == InternalType.SHELL);
+    }
+
+    public static boolean isDriveLetterPath(String path) {
+        return path.length() >= 2 && Character.isLetter(path.charAt(0)) && path.charAt(1) == ':';
+    }
+        
+}
diff --git a/src/main/java/jnr/posix/windows/CommonFileInformation.java b/src/main/java/jnr/posix/windows/CommonFileInformation.java
new file mode 100644
index 0000000..9932163
--- /dev/null
+++ b/src/main/java/jnr/posix/windows/CommonFileInformation.java
@@ -0,0 +1,101 @@
+package jnr.posix.windows;

+

+import static jnr.posix.FileStat.*;

+

+/**

+ * Shared logic between by handle and file path FILE_INFORMATION.

+ */

+public abstract class CommonFileInformation extends jnr.ffi.Struct {

+    public static int FILE_ATTRIBUTE_READONLY  = 0x01;

+    public static int FILE_ATTRIBUTE_DIRECTORY = 0x10;

+

+    public class HackyFileTime {

+        private final UnsignedLong dwHighDateTime;

+        private final UnsignedLong dwLowDateTime;

+

+        public HackyFileTime(UnsignedLong high, UnsignedLong low) {

+            this.dwHighDateTime = high;

+            this.dwLowDateTime = low;

+        }

+

+        public long getLowDateTime() {

+            return dwLowDateTime.longValue();

+        }

+

+        public long getHighDateTime() {

+            return dwHighDateTime.longValue();

+        }

+

+        public long getLongValue() {

+            return (getHighDateTime() & 0xFFFFFFFFL) << 32 | (getLowDateTime() & 0xFFFFFFFFL);

+        }

+    }

+

+

+    protected CommonFileInformation(jnr.ffi.Runtime runtime) {

+        super(runtime);

+    }

+

+    public abstract int getFileAttributes();

+    public abstract HackyFileTime getCreationTime();

+    public abstract HackyFileTime getLastAccessTime();

+    public abstract HackyFileTime getLastWriteTime();

+    public abstract long getFileSizeHigh();

+    public abstract long getFileSizeLow();

+

+    public int getMode(java.lang.String path) {

+        int attr = getFileAttributes();

+        int mode = S_IRUSR;

+

+        if ((attr & FILE_ATTRIBUTE_READONLY) == 0) {

+            mode |= S_IWUSR;

+

+        }

+        mode |= (attr & FILE_ATTRIBUTE_DIRECTORY) != 0 ? (S_IFDIR | S_IXUSR) : S_IFREG;

+

+        path = path.toLowerCase();

+        if (path != null && (mode & S_IFREG) != 0 &&

+                (path.endsWith(".bat") || path.endsWith(".cmd") || path.endsWith(".com") || path.endsWith(".exe"))) {

+            mode |= S_IXUSR;

+        }

+

+        mode |= (mode & 0700) >> 3;

+        mode |= (mode & 0700) >> 6;

+

+        return mode;

+    }

+

+    public long getLastWriteTimeNanoseconds() {

+        return epochNanos(getLastWriteTime().getLongValue());

+    }

+

+    public long getLastAccessTimeNanoseconds() {

+        return epochNanos(getLastAccessTime().getLongValue());

+    }

+

+    public long getCreationTimeNanoseconds() {

+        return epochNanos(getCreationTime().getLongValue());

+    }

+

+    public long getFileSize() {

+        return (getFileSizeHigh() << 32) | getFileSizeLow();

+    }

+

+    // FIXME: I used same equation in C to get number.  I did something wrong with the math here in Java

+    //private static final int HOURS = 24;

+    //private static final int MINUTES = 60;

+    // private static final int SECONDS = 60;

+    public static final int NANOSECONDS = 1000 * 1000 * 1000;

+    // on number of days a year: https://imicrothinking.wordpress.com/tag/365-2425-days/

+    private static final double DAYS_BETWEEN_WINDOWS_AND_UNIX = (1970 - 1601) * 365.2425;

+    private static final long NANOSECONDS_TO_UNIX_EPOCH_FROM_WINDOWS = 11644473600L * NANOSECONDS;

+           // (long) (DAYS_BETWEEN_WINDOWS_AND_UNIX * HOURS * SECONDS * MINUTES * MICROSECONDS);

+

+    private long epochNanos(long windowsNanoChunks) {

+        return (windowsNanoChunks * 100) - NANOSECONDS_TO_UNIX_EPOCH_FROM_WINDOWS;

+    }

+

+    public static long asNanoSeconds(long seconds) {

+        return (seconds * 1000 + NANOSECONDS_TO_UNIX_EPOCH_FROM_WINDOWS / 1000) * 10;

+    }

+}

diff --git a/src/main/java/jnr/posix/windows/SystemTime.java b/src/main/java/jnr/posix/windows/SystemTime.java
new file mode 100644
index 0000000..2dfae63
--- /dev/null
+++ b/src/main/java/jnr/posix/windows/SystemTime.java
@@ -0,0 +1,25 @@
+package jnr.posix.windows;

+

+import jnr.ffi.*;

+

+/**

+ * Created by enebo on 9/18/2015.

+ */

+public class SystemTime extends jnr.ffi.Struct {

+    Unsigned16 wYear = new Unsigned16();

+    Unsigned16 wMonth = new Unsigned16();

+    Unsigned16 wDayOfWeek = new Unsigned16();

+    Unsigned16 wDay = new Unsigned16();

+    Unsigned16 wHour = new Unsigned16();

+    Unsigned16 wMinute = new Unsigned16();

+    Unsigned16 wSecond = new Unsigned16();

+    Unsigned16 wMilliseconds = new Unsigned16();

+    

+    public SystemTime(jnr.ffi.Runtime runtime) {

+        super(runtime);

+    }

+

+    public java.lang.String toString() {

+        return "" + wYear + "/" + wMonth + "/" + wDay + " " + wHour + ":" + wMinute + ":" + wSecond;

+    }

+}

diff --git a/src/main/java/jnr/posix/windows/WindowsByHandleFileInformation.java b/src/main/java/jnr/posix/windows/WindowsByHandleFileInformation.java
new file mode 100644
index 0000000..2b9b820
--- /dev/null
+++ b/src/main/java/jnr/posix/windows/WindowsByHandleFileInformation.java
@@ -0,0 +1,50 @@
+package jnr.posix.windows;

+

+/**

+ * BY_HANDLE_FILE_INFORMATION

+ */

+public class WindowsByHandleFileInformation extends CommonFileInformation {

+    final Unsigned32 dwFileAttributes = new Unsigned32();

+    // FIXME: I have no idea why I could not include FileTime here but having it do its own layout seems to change

+    // something.

+    final UnsignedLong chigh = new UnsignedLong();

+    final UnsignedLong clow = new UnsignedLong();

+    final UnsignedLong ahigh = new UnsignedLong();

+    final UnsignedLong alow = new UnsignedLong();

+    final UnsignedLong uhigh = new UnsignedLong();

+    final UnsignedLong ulow = new UnsignedLong();

+    final Unsigned32 dwVolumeSerialNumber = new Unsigned32();

+    final Unsigned32 nFileSizeHigh = new Unsigned32();

+    final Unsigned32 nFileSizeLow = new Unsigned32();

+    final Unsigned32 nNumberOfLinks = new Unsigned32();

+    final Unsigned32 nFileIndexHigh = new Unsigned32();

+    final Unsigned32 nFileIndexLow = new Unsigned32();

+

+    public WindowsByHandleFileInformation(jnr.ffi.Runtime runtime) {

+        super(runtime);

+    }

+

+    public HackyFileTime getCreationTime() {

+        return new HackyFileTime(chigh, clow);

+    }

+

+    public HackyFileTime getLastAccessTime() {

+        return new HackyFileTime(ahigh, alow);

+    }

+

+    public HackyFileTime getLastWriteTime() {

+        return new HackyFileTime(uhigh, ulow);

+    }

+

+    public int getFileAttributes() {

+        return dwFileAttributes.intValue();

+    }

+

+    public long getFileSizeHigh() {

+        return nFileSizeHigh.intValue();

+    }

+

+    public long getFileSizeLow() {

+        return nFileSizeLow.intValue();

+    }

+}

diff --git a/src/main/java/jnr/posix/windows/WindowsFileInformation.java b/src/main/java/jnr/posix/windows/WindowsFileInformation.java
new file mode 100644
index 0000000..d719e41
--- /dev/null
+++ b/src/main/java/jnr/posix/windows/WindowsFileInformation.java
@@ -0,0 +1,58 @@
+package jnr.posix.windows;

+

+/**

+ * WIN32_FILE_ATTRIBUTE_DATA

+ */

+

+public class WindowsFileInformation extends CommonFileInformation {

+

+    final UnsignedLong dwFileAttributes;

+    // FIXME: I have no idea why I could not include FileTime here but having it do its own layout seems to change

+    // something.

+    final UnsignedLong chigh;

+    final UnsignedLong clow;

+    final UnsignedLong ahigh;

+    final UnsignedLong alow;

+    final UnsignedLong uhigh;

+    final UnsignedLong ulow;

+    final UnsignedLong nFileSizeHigh;

+    final UnsignedLong nFileSizeLow;

+

+    public WindowsFileInformation(jnr.ffi.Runtime runtime) {

+        super(runtime);

+

+        dwFileAttributes = new UnsignedLong();

+        clow = new UnsignedLong();

+        chigh = new UnsignedLong();

+        alow = new UnsignedLong();

+        ahigh = new UnsignedLong();

+        ulow = new UnsignedLong();

+        uhigh = new UnsignedLong();

+        nFileSizeHigh = new UnsignedLong();

+        nFileSizeLow = new UnsignedLong();

+    }

+

+    public HackyFileTime getCreationTime() {

+        return new HackyFileTime(chigh, clow);

+    }

+

+    public HackyFileTime getLastAccessTime() {

+        return new HackyFileTime(ahigh, alow);

+    }

+

+    public HackyFileTime getLastWriteTime() {

+        return new HackyFileTime(uhigh, ulow);

+    }

+

+    public int getFileAttributes() {

+        return dwFileAttributes.intValue();

+    }

+

+    public long getFileSizeHigh() {

+        return nFileSizeHigh.longValue();

+    }

+

+    public long getFileSizeLow() {

+        return nFileSizeLow.longValue();

+    }

+}

diff --git a/src/main/java/jnr/posix/windows/WindowsFileTime.java b/src/main/java/jnr/posix/windows/WindowsFileTime.java
new file mode 100644
index 0000000..86b21d0
--- /dev/null
+++ b/src/main/java/jnr/posix/windows/WindowsFileTime.java
@@ -0,0 +1,25 @@
+package jnr.posix.windows;

+

+/**

+ * FILETIME

+ */

+public class WindowsFileTime extends jnr.ffi.Struct {

+    final Unsigned32 lowDateTime = new Unsigned32();

+    final Unsigned32 highDateTime = new Unsigned32();

+

+    public WindowsFileTime(jnr.ffi.Runtime runtime) {

+        super(runtime);

+    }

+

+    public int getLowDateTime() {

+        return lowDateTime.intValue();

+    }

+

+    public int getHighDateTime() {

+        return highDateTime.intValue();

+    }

+

+    public long getLongValue() {

+        return getHighDateTime() << 32 + getLowDateTime();

+    }

+}

diff --git a/src/main/java/jnr/posix/windows/WindowsFindData.java b/src/main/java/jnr/posix/windows/WindowsFindData.java
new file mode 100644
index 0000000..1a5de7a
--- /dev/null
+++ b/src/main/java/jnr/posix/windows/WindowsFindData.java
@@ -0,0 +1,77 @@
+package jnr.posix.windows;

+

+import jnr.ffi.*;

+

+/**

+ * WIN32_FIND_DATA.  For use with FindFirstFileW and friends (since this is for W methods

+ * the filename fields are wchar_t (or on windows usigned short) in width - TCHAR is an ambiguously

+ * sized type depending on which variant of method calls it).

+ */

+public class WindowsFindData extends CommonFileInformation {

+    public static final int MAX_PATH = 260;

+

+    final UnsignedLong dwFileAttributes;

+    // FIXME: I have no idea why I could not include FileTime here but having it do its own layout seems to change

+    // something.

+    final UnsignedLong chigh;

+    final UnsignedLong clow;

+    final UnsignedLong ahigh;

+    final UnsignedLong alow;

+    final UnsignedLong uhigh;

+    final UnsignedLong ulow;

+    final UnsignedLong nFileSizeHigh;

+    final UnsignedLong nFileSizeLow;

+    final UnsignedLong dwReserved0;

+    final UnsignedLong dwReserved1;

+    final Padding cFileName;

+    final Padding cAlternateFileName;

+

+    public WindowsFindData(jnr.ffi.Runtime runtime) {

+        super(runtime);

+

+        dwFileAttributes = new UnsignedLong();

+        clow = new UnsignedLong();

+        chigh = new UnsignedLong();

+        alow = new UnsignedLong();

+        ahigh = new UnsignedLong();

+        ulow = new UnsignedLong();

+        uhigh = new UnsignedLong();

+        nFileSizeHigh = new UnsignedLong();

+        nFileSizeLow = new UnsignedLong();

+        dwReserved0 = new UnsignedLong();

+        dwReserved1 = new UnsignedLong();

+        // This is epically large but any paths with //?/ can get a long name.  Also even if you do not

+        // and depend on MAX_PATH (original constant this struct is supposedly defined on) then you can

+        // get overflows from FindFirstFileW on reparse points.  So we will waste memory on allocation

+        // to avoid the potential for buffer overflows.

+        // This number specifically is the actual physical limit of a file size in NTFS.  So although this

+        // the functions using this struct cannot handle something this long the internet seems to think

+        // it is possible to get these long values copied into this field.

+        cFileName = new Padding(NativeType.USHORT, 32767);

+        cAlternateFileName = new Padding(NativeType.USHORT, 14);

+    }

+

+    public HackyFileTime getCreationTime() {

+        return new HackyFileTime(chigh, clow);

+    }

+

+    public HackyFileTime getLastAccessTime() {

+        return new HackyFileTime(ahigh, alow);

+    }

+

+    public HackyFileTime getLastWriteTime() {

+        return new HackyFileTime(uhigh, ulow);

+    }

+

+    public int getFileAttributes() {

+        return dwFileAttributes.intValue();

+    }

+

+    public long getFileSizeHigh() {

+        return nFileSizeHigh.longValue();

+    }

+

+    public long getFileSizeLow() {

+        return nFileSizeLow.longValue();

+    }

+}

diff --git a/src/test/java/jnr/posix/ConfstrTest.java b/src/test/java/jnr/posix/ConfstrTest.java
new file mode 100644
index 0000000..b616496
--- /dev/null
+++ b/src/test/java/jnr/posix/ConfstrTest.java
@@ -0,0 +1,34 @@
+package jnr.posix;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+import java.nio.ByteBuffer;
+import jnr.constants.platform.Confstr;
+import jnr.constants.platform.Errno;
+import jnr.posix.util.Platform;
+
+public class ConfstrTest {
+    private static POSIX posix;
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        posix = POSIXFactory.getPOSIX(new DummyPOSIXHandler(), true);
+    }
+
+    @Test
+    public void confstr() {
+        if (Platform.IS_WINDOWS) {
+            int len = posix.confstr(Confstr._CS_PATH, null, 0);
+            assertEquals(-1, len);
+            assertEquals(Errno.EOPNOTSUPP.intValue(), posix.errno());
+        } else {
+            int len = posix.confstr(Confstr._CS_PATH, null, 0);
+            assertTrue("bad strlen", len > 0);
+
+            ByteBuffer buf = ByteBuffer.allocate(len);
+            posix.confstr(Confstr._CS_PATH, buf, len);
+            String str = new String(buf.array());
+            assertTrue("CS_PATH is blank", str.length() > 0);
+        }
+    }
+}
diff --git a/src/test/java/jnr/posix/CryptTest.java b/src/test/java/jnr/posix/CryptTest.java
new file mode 100644
index 0000000..f73c7b0
--- /dev/null
+++ b/src/test/java/jnr/posix/CryptTest.java
@@ -0,0 +1,40 @@
+package jnr.posix;
+
+import jnr.posix.util.Platform;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+/**
+ * Created by headius on 3/24/15.
+ */
+public class CryptTest {
+    @Before
+    public void before() {
+        posix = POSIXFactory.getPOSIX();
+    }
+    @Test
+    public void testCrypt() {
+        if (!Platform.IS_WINDOWS) {
+            String str1 = "blahblahblah";
+            String salt1 = "saltysalty";
+
+            CharSequence result1 = posix.crypt(str1, salt1);
+            Assert.assertNotNull(result1);
+
+            byte[] str1bytes = Arrays.copyOfRange(str1.getBytes(), 0, str1.length() + 1);
+            byte[] salt1bytes = Arrays.copyOfRange(salt1.getBytes(), 0, salt1.length() + 1);
+            byte[] result2 = posix.crypt(str1bytes, salt1bytes);
+
+            Assert.assertNotNull(result2);
+
+            String result2str = new String(result2, 0, result2.length - 1);
+
+            Assert.assertEquals(result1, result2str);
+        }
+    }
+
+    private POSIX posix;
+}
diff --git a/src/test/java/jnr/posix/DTableSizeTest.java b/src/test/java/jnr/posix/DTableSizeTest.java
new file mode 100644
index 0000000..7b05d27
--- /dev/null
+++ b/src/test/java/jnr/posix/DTableSizeTest.java
@@ -0,0 +1,22 @@
+package jnr.posix;
+
+import jnr.posix.util.Platform;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assume.assumeTrue;
+
+public class DTableSizeTest {
+    @Test
+    public void testGetDtableSize() throws Exception
+    {
+        if (!Platform.IS_WINDOWS) { // FIXME: I have seen window's impl so this can be impld
+            POSIX posix = POSIXFactory.getPOSIX(new DummyPOSIXHandler(), true);
+            assumeTrue("getdtablesize only supported on native posix", posix.isNative());
+
+            int dtablesize = posix.getdtablesize();
+            assertThat(dtablesize, not(-1));
+        }
+    }
+}
diff --git a/src/test/java/jnr/posix/DummyPOSIXHandler.java b/src/test/java/jnr/posix/DummyPOSIXHandler.java
new file mode 100644
index 0000000..737c915
--- /dev/null
+++ b/src/test/java/jnr/posix/DummyPOSIXHandler.java
@@ -0,0 +1,64 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package jnr.posix;
+
+import jnr.constants.platform.Errno;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.PrintStream;
+
+/**
+ *
+ * @author wayne
+ */
+public class DummyPOSIXHandler implements POSIXHandler {
+
+    public void error(Errno error, String extraData) {
+        throw new UnsupportedOperationException("error: " + error);
+    }
+    
+    public void error(Errno error, String methodName, String extraData) {
+        throw new UnsupportedOperationException("error: " + error);
+    }    
+
+    public void unimplementedError(String methodName) {
+        throw new UnsupportedOperationException("unimplemented method: " + methodName);
+    }
+
+    public void warn(WARNING_ID id, String message, Object... data) {
+        throw new UnsupportedOperationException("warning: " + id);
+    }
+
+    public boolean isVerbose() {
+        return true;
+    }
+
+    public File getCurrentWorkingDirectory() {
+        return new File("/tmp");
+    }
+
+    public String[] getEnv() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    public InputStream getInputStream() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    public PrintStream getOutputStream() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    public int getPID() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    public PrintStream getErrorStream() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+}
diff --git a/src/test/java/jnr/posix/EnvTest.java b/src/test/java/jnr/posix/EnvTest.java
new file mode 100644
index 0000000..47280c1
--- /dev/null
+++ b/src/test/java/jnr/posix/EnvTest.java
@@ -0,0 +1,83 @@
+package jnr.posix;
+
+import com.kenai.jffi.MemoryIO;
+import jnr.ffi.Pointer;
+import jnr.posix.util.Platform;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class EnvTest {
+    private static POSIX posix;
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        posix = POSIXFactory.getPOSIX(new DummyPOSIXHandler(), true);
+    }
+
+    @Test
+    public void testGetenv() throws Throwable {
+        assertNotNull(posix.getenv("PATH"));
+        assertNull(posix.getenv("SOME_NON_EXISTENT_ENV"));
+    }
+
+    @Test
+    public void testSetenvNonOverwrite() throws Throwable {
+        final String path = posix.getenv("PATH");
+        int result = posix.setenv("PATH", "new value", 0);
+
+        assertEquals(0, result);
+        assertEquals(path, posix.getenv("PATH"));
+    }
+
+    @Test
+    public void testSetenvOverwrite() throws Throwable {
+        if (!Platform.IS_WINDOWS) {
+            final String path = posix.getenv("PATH");
+            int result = posix.setenv("PATH", "new value", 1);
+
+            assertEquals(0, result);
+            assertNotEquals(path, posix.getenv("PATH"));
+            posix.setenv("PATH", path, 1);
+        }
+    }
+
+    @Test
+    public void testSetEnvNewVar() throws Throwable {
+        if (!Platform.IS_WINDOWS) {
+            int result = posix.setenv("MY_NEW_SETENV_VAR", "Yo", 0);
+
+            assertEquals(0, result);
+            assertEquals("Yo", posix.getenv("MY_NEW_SETENV_VAR"));
+        }
+    }
+
+    @Test
+    public void testUnsetenv() throws Throwable {
+        if (!Platform.IS_WINDOWS) {
+            posix.setenv("MY_UNSETENV_VAR", "Yo", 1);
+            assertEquals("Yo", posix.getenv("MY_UNSETENV_VAR"));
+
+            int result = posix.unsetenv("MY_UNSETENV_VAR");
+            assertEquals(0, result);
+
+            assertNull(posix.getenv("MY_UNSETENV_VAR"));
+        }
+    }
+
+    @Test
+    public void testEnv() throws Throwable {
+        final Pointer env = posix.environ();
+
+        int offset = 0;
+        while (env.getPointer(offset) != null) {
+            final Pointer entryPointer = env.getPointer(offset);
+            final String entry = new String(MemoryIO.getInstance().getZeroTerminatedByteArray(entryPointer.address()));
+
+            assertTrue(entry.contains("="));
+
+            offset += jnr.ffi.Runtime.getSystemRuntime().addressSize();
+        }
+    }
+}
diff --git a/src/test/java/jnr/posix/FileStatTest.java b/src/test/java/jnr/posix/FileStatTest.java
new file mode 100644
index 0000000..3db2331
--- /dev/null
+++ b/src/test/java/jnr/posix/FileStatTest.java
@@ -0,0 +1,202 @@
+
+package jnr.posix;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import jnr.posix.util.FieldAccess;
+import jnr.posix.util.Platform;
+import org.junit.*;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.lang.reflect.Field;
+
+import static org.junit.Assert.*;
+
+public class FileStatTest {
+
+    public FileStatTest() {
+    }
+
+    private void addNBytes(File file, int amount) {
+        FileOutputStream fis = null;
+
+        try {
+            fis = new FileOutputStream(file);
+            byte[] buf = new byte[amount];
+            fis.write(buf);
+        } catch (IOException e) {
+            if (fis != null) { try { fis.close(); } catch (IOException e2) {} }
+        }
+    }
+
+    private static POSIX posix;
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        posix = POSIXFactory.getPOSIX(new DummyPOSIXHandler(), true);
+    }
+
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+    }
+
+    @Before
+    public void setUp() {
+    }
+
+    @After
+    public void tearDown() {
+    }
+
+    @Test
+    public void filestat() throws Throwable {
+        File f = File.createTempFile("stat", null);
+        int size = 1567;
+        //Thread.sleep(2000);
+        addNBytes(f, size);
+        try {
+            FileStat stat = posix.stat(f.getAbsolutePath());
+            assertNotNull("posix.stat failed", stat);
+            assertEquals(size, stat.st_size());
+            assertEquals(1, stat.nlink());
+            //assertNotEquals(stat.mtime(), stat.ctime());
+
+            stat = posix.allocateStat();
+            int result = posix.stat(f.getAbsolutePath(), stat);
+            assertNotNull("posix.stat failed", stat);
+            assertEquals(0, result);
+            assertEquals(size, stat.st_size());
+        } finally {
+            f.delete();
+        }
+    }
+
+    @Test
+    public void filestatDescriptor() throws Throwable {
+        File f = File.createTempFile("stat", null);
+
+        try {
+            FileInputStream fis = new FileInputStream(f);
+            FileStat stat = posix.allocateStat();
+            int result = posix.fstat(fis.getFD(), stat);
+            assertEquals(0, result);
+            assertEquals(0, stat.st_size());
+        } finally {
+            f.delete();
+        }
+
+    }
+
+    @Test
+    public void filestatInt() throws Throwable {
+        // Windows does not store fd in FileDescriptor so this test wll not work
+        if (jnr.ffi.Platform.getNativePlatform().isUnix()) {
+            File f = File.createTempFile("stat", null);
+            try {
+                FileInputStream fis = new FileInputStream(f);
+                FileDescriptor desc = fis.getFD();
+                int fd = -1;
+                try {
+                    Field fdField = FieldAccess.getProtectedField(FileDescriptor.class, "fd");
+                    fd = fdField.getInt(desc);
+                } catch (SecurityException e) {
+                } catch (IllegalArgumentException e) {
+                } catch (IllegalAccessException e) {
+                }
+                FileStat stat = posix.allocateStat();
+                int result = posix.fstat(fd, stat);
+                assertTrue(fd > 2); // should not be stdin, stdout, stderr
+                assertEquals(0, result);
+            } finally {
+                f.delete();
+            }
+        } else {
+            FileStat stat = posix.fstat(0);
+            assertTrue(stat != null);
+        }
+    }
+
+    // FIXME: I could guarantee this does not exist but this was very very quick :)
+    private static final String NON_EXISTENT_FILENAME = "skdjlfklfsdjk";
+    @Test
+    public void filestatFailed() throws Throwable {
+        FileStat stat = null;
+
+        // A little wonky without adding a new posixhandler but we are using dummy so this is ok for now
+        // the default handler raises on a problem in stat so we are only verifying this at the moment.
+        try {
+            stat = posix.stat(NON_EXISTENT_FILENAME);
+        } catch (UnsupportedOperationException e) {}
+
+        assertTrue(stat == null);
+    }
+
+    
+    @Test
+    public void fileStatNanoTime() throws Throwable {
+        // Currently only linux file stat support nano time resolution
+        jnr.ffi.Platform nativePlatform = jnr.ffi.Platform.getNativePlatform();
+        if (nativePlatform.getOS() == jnr.ffi.Platform.OS.LINUX) {
+            File f = File.createTempFile("stat", null);
+            try {
+                FileStat st = posix.stat(f.getAbsolutePath());
+                assertNotNull("posix.stat failed", st);
+
+                FileStat stat = posix.allocateStat();
+                int result = posix.stat(f.getAbsolutePath(), stat);
+                assertNotNull("posix.stat failed", st);
+                assertEquals(0, result);
+
+                NanosecondFileStat fstat32 = (NanosecondFileStat) stat;
+                assertTrue(fstat32.cTimeNanoSecs() > 0);
+                assertTrue(fstat32.mTimeNanoSecs() > 0);
+                assertTrue(fstat32.aTimeNanoSecs() > 0);
+                assertEquals(fstat32.cTimeNanoSecs(), fstat32.mTimeNanoSecs());
+            } finally {
+                f.delete();
+            }
+        }
+    }
+
+    @Test
+    public void structStatSize() throws Throwable {
+        if (Platform.IS_SOLARIS) {
+            jnr.ffi.Runtime runtime = jnr.ffi.Runtime.getSystemRuntime();
+            if (Platform.IS_32_BIT) {
+                assertEquals("struct size is wrong", 144, new SolarisFileStat32.Layout(runtime).size());
+            } else {
+                assertEquals("struct size is wrong", 128, new SolarisFileStat64.Layout(runtime).size());
+            }
+        }
+
+        if (Platform.IS_SOLARIS) {
+            File f = File.createTempFile("stat", null);
+            try {
+                FileStat st = posix.stat(f.getAbsolutePath());
+
+                if (Platform.IS_32_BIT) {
+                    assertSame("incorrect stat instance returned", SolarisFileStat32.class, st.getClass());
+                } else {
+                    assertSame("incorrect stat instance returned", SolarisFileStat64.class, st.getClass());
+                }
+            } finally {
+                f.delete();
+            }
+        }
+    }
+
+    @Test
+    public void filestatDirectory() throws Throwable {
+        File f = File.createTempFile("stat", null).getParentFile();
+        try {
+            FileStat stat = posix.stat(f.getAbsolutePath());
+
+            assertTrue(stat.isDirectory());
+        } finally {
+            f.delete();
+        }
+    }
+}
diff --git a/src/test/java/jnr/posix/FileTest.java b/src/test/java/jnr/posix/FileTest.java
new file mode 100644
index 0000000..afadb07
--- /dev/null
+++ b/src/test/java/jnr/posix/FileTest.java
@@ -0,0 +1,680 @@
+package jnr.posix;
+
+import jnr.constants.platform.Access;
+import jnr.constants.platform.Fcntl;
+import jnr.constants.platform.Errno;
+import jnr.constants.platform.OpenFlags;
+import jnr.ffi.Pointer;
+import jnr.posix.util.Platform;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.*;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.charset.Charset;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+public class FileTest {
+    private static POSIX posix;
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        posix = POSIXFactory.getPOSIX(new DummyPOSIXHandler(), true);
+    }
+
+    @Test
+    public void utimesTest() throws Throwable {
+        // FIXME: On Windows this is working but providing wrong numbers and therefore getting wrong results.
+        if (!Platform.IS_WINDOWS) {
+            File f = File.createTempFile("utimes", null);
+
+            int rval = posix.utimes(f.getAbsolutePath(), new long[]{800, 200}, new long[]{900, 300});
+            assertEquals("utimes did not return 0", 0, rval);
+
+            FileStat stat = posix.stat(f.getAbsolutePath());
+
+            assertEquals("atime seconds failed", 800, stat.atime());
+            assertEquals("mtime seconds failed", 900, stat.mtime());
+
+            // The nano secs part is available in other stat implementations. We really just want to verify that the
+            // nsec portion of the timeval is passed through to the POSIX call.
+            // Mac seems to fail this test sporadically.
+            if (stat instanceof NanosecondFileStat && !Platform.IS_MAC) {
+                NanosecondFileStat linuxStat = (NanosecondFileStat) stat;
+
+                assertEquals("atime useconds failed", 200000, linuxStat.aTimeNanoSecs());
+                assertEquals("mtime useconds failed", 300000, linuxStat.mTimeNanoSecs());
+            }
+
+            f.delete();
+        }
+    }
+
+    @Test
+    public void utimesDefaultValuesTest() throws Throwable {
+        if (!Platform.IS_WINDOWS) {
+            File f = File.createTempFile("utimes", null);
+
+            long oldTime = posix.stat(f.getAbsolutePath()).mtime();
+            Thread.sleep(2000);
+
+            int rval = posix.utimes(f.getAbsolutePath(), null, null);
+            assertEquals("utimes did not return 0", 0, rval);
+
+            FileStat stat = posix.stat(f.getAbsolutePath());
+
+            assertTrue("atime failed", stat.atime() > oldTime);
+            assertTrue("mtime failed", stat.mtime() > oldTime);
+
+            f.delete();
+        }
+    }
+
+    @Test
+    public void utimesPointerTest() throws Throwable {
+        if (!Platform.IS_WINDOWS) {
+            File f = File.createTempFile("utimes", null);
+
+            Pointer times = jnr.ffi.Runtime.getSystemRuntime().getMemoryManager().allocateDirect(8 * 4); // long[2][2] == 4 longs.
+            times.putLong(0, 800);
+            times.putLong(8, 200);
+            times.putLong(16, 900);
+            times.putLong(24, 300);
+
+            int rval = posix.utimes(f.getAbsolutePath(), times);
+            assertEquals("utimes did not return 0", 0, rval);
+
+            FileStat stat = posix.stat(f.getAbsolutePath());
+
+            assertEquals("atime seconds failed", 800, stat.atime());
+            assertEquals("mtime seconds failed", 900, stat.mtime());
+
+            // The nano secs part is available in other stat implementations.  We use Linux x86_64 because it's
+            // representative.  We really just want to verify that the usec portion of the timeval is passed throug
+            // to the POSIX call.
+            if (hasNanosecondPrecision(stat)) {
+                NanosecondFileStat linuxStat = (NanosecondFileStat) stat;
+
+//                assertEquals("atime useconds failed", 200000, linuxStat.aTimeNanoSecs());
+                assertEquals("mtime useconds failed", 300000, linuxStat.mTimeNanoSecs());
+            }
+
+            f.delete();
+        }
+    }
+
+    @Test
+    public void utimensatAbsolutePath() throws Throwable {
+        File file = File.createTempFile("utimensat", null);
+        utimensat(file, 0);
+    }
+
+    @Test
+    public void utimensatRelativePath() throws Throwable {
+        String path = "utimensat";
+        File file = new File(path);
+        file.createNewFile();
+        int parentFd = posix.open(".", OpenFlags.O_DIRECTORY.intValue(), 0444);
+        utimensat(file, parentFd);
+    }
+
+    @Test
+    public void futimens() throws Throwable {
+        File file = File.createTempFile("futimens", null);
+        FileStat fileStat = posix.stat(file.getPath());
+        if (!hasNanosecondPrecision(fileStat)) {
+            file.delete();
+            return;
+        }
+
+        long atimeSeconds = fileStat.atime()+1;
+        long mtimeSeconds = fileStat.mtime()-1;
+        long atimeNanoSeconds = 123456789;
+        long mtimeNanoSeconds = 135;
+        int fd = posix.open(file.getAbsolutePath(), OpenFlags.O_RDWR.intValue(), 0444);
+        posix.futimens(fd,
+                new long[] {atimeSeconds, atimeNanoSeconds},
+                new long[] {mtimeSeconds, mtimeNanoSeconds});
+        assertStatNanoSecond(file, atimeSeconds, atimeNanoSeconds, mtimeSeconds, mtimeNanoSeconds);
+    }
+
+    @Test
+    public void lutimesTest() throws Throwable {
+        // FIXME: On Windows this is working but providing wrong numbers and therefore getting wrong results.
+        if (!Platform.IS_WINDOWS) {
+            File f1 = File.createTempFile("lutimes", null);
+            File f2 = new File(f1.getParentFile(), "lutimes-link");
+            posix.symlink(f1.getAbsolutePath(), f2.getAbsolutePath());
+
+            int rval = posix.utimes(f1.getAbsolutePath(), new long[]{800, 0}, new long[]{900, 0});
+            assertEquals("utimes did not return 0", 0, rval);
+
+            rval = posix.lutimes(f2.getAbsolutePath(), new long[]{1800, 0}, new long[]{1900, 0});
+            assertEquals("lutimes did not return 0", 0, rval);
+
+            FileStat stat = posix.stat(f1.getAbsolutePath());
+            assertEquals("atime seconds failed", 800, stat.atime());
+            assertEquals("mtime seconds failed", 900, stat.mtime());
+
+            stat = posix.lstat(f2.getAbsolutePath());
+            assertEquals("atime seconds failed", 1800, stat.atime());
+            assertEquals("mtime seconds failed", 1900, stat.mtime());
+
+            f1.delete();
+            f2.delete();
+        }
+    }
+
+    @Test
+    public void futimeTest() throws Throwable {
+        if (!Platform.IS_WINDOWS) {
+            File f = File.createTempFile("jnr-posix-futime", "tmp");
+            long oldTime = posix.stat(f.getAbsolutePath()).mtime();
+            Thread.sleep(2000);
+            int fd = posix.open(f.getAbsolutePath(), OpenFlags.O_RDWR.intValue(), 0666);
+            int rval = posix.futimes(fd, null, null);
+            assertEquals("futime did not return 0", 0, rval);
+            long newTime = posix.stat(f.getAbsolutePath()).mtime();
+            f.delete();
+            assertTrue("mtime failed", newTime > oldTime);
+        }
+    }
+
+    @Test
+    public void linkTest() throws Throwable {
+        File f1 = File.createTempFile("utime", null);
+        File f2 = new File(f1.getAbsolutePath() + "link");
+        int rval = posix.link(f1.getAbsolutePath(), f2.getAbsolutePath());
+        assertEquals("link did not return 0", 0, rval);
+        assertTrue("Link was not created", f2.exists());
+        f1.delete();
+        f2.delete();
+    }
+
+    @Test
+    public void mkdirRelativeTest() throws Throwable {
+        File dir = new File("tmp");
+        int rval = posix.mkdir(dir.getPath(), 0);
+        assertEquals("mkdir did not return 0", 0, rval);
+        assertTrue("Directory was not created", dir.exists());
+        dir.delete();
+    }
+
+    @Test
+    public void mkdirAbsoluteTest() throws Throwable {
+        File dir = new File("tmp");
+        int rval = posix.mkdir(dir.getAbsolutePath(), 0);
+        assertEquals("mkdir did not return 0", 0, rval);
+        assertTrue("Directory was not created", dir.exists());
+        dir.delete();
+    }
+    
+    @Test
+    public void flockTest() throws Throwable {
+        if (!Platform.IS_WINDOWS) {
+            File tmp = File.createTempFile("flockTest", "tmp");
+            RandomAccessFile raf = new RandomAccessFile(tmp, "rw");
+            RandomAccessFile raf2 = new RandomAccessFile(tmp, "rw");
+            FileChannel fc = raf.getChannel();
+            FileChannel fc2 = raf2.getChannel();
+            FileDescriptor FD = JavaLibCHelper.getDescriptorFromChannel(fc);
+            FileDescriptor FD2 = JavaLibCHelper.getDescriptorFromChannel(fc2);
+            int fd = JavaLibCHelper.getfdFromDescriptor(FD);
+            int fd2 = JavaLibCHelper.getfdFromDescriptor(FD2);
+
+            assertEquals(0, posix.flock(fd, 1)); // LOCK_SH
+            assertEquals(0, posix.flock(fd, 8)); // LOCK_UN
+            assertEquals(0, posix.flock(fd, 2)); // LOCK_EX
+            assertEquals(-1, posix.flock(fd2, 2 | 4)); // LOCK_EX | LOCK_NB
+            assertEquals(0, posix.flock(fd, 8)); // LOCK_UN
+        }
+    }
+
+    @Test
+    public void dupTest() throws Throwable {
+        File tmp = File.createTempFile("dupTest", "tmp");
+        RandomAccessFile raf = new RandomAccessFile(tmp, "rw");
+        FileChannel fileChannel = raf.getChannel();
+        int fileDescriptor = getFdFromDescriptor(JavaLibCHelper.getDescriptorFromChannel(fileChannel));
+
+        byte[] outContent = "foo".getBytes();
+
+        FileDescriptor newFileDescriptor = toDescriptor(posix.dup(fileDescriptor));
+
+        new FileOutputStream(toDescriptor(fileDescriptor)).write(outContent);
+        raf.seek(0);
+
+        byte[] inContent = new byte[outContent.length];
+        new FileInputStream(newFileDescriptor).read(inContent, 0, 3);
+
+        assertArrayEquals(inContent, outContent);
+    }
+
+    public static final int SEEK_SET = 0;
+
+    @Test
+    public void dup2Test() throws Throwable {
+        File tmp = File.createTempFile("dupTest", "tmp");
+        int oldFd = posix.open(tmp.getAbsolutePath(), OpenFlags.O_RDWR.intValue(), 0666);
+        int newFd = 1000;
+
+        byte[] outContent = "foo".getBytes();
+
+        // NB: Windows differs a bit from POSIX's return value.  Both will return -1 on failure, but Windows will return
+        // 0 upon success, while POSIX will return the new FD.  Since we already know what the FD will be if the call
+        // is successful, it's easy to make code that works with both forms.  But it is something to watch out for.
+        assertNotEquals(-1, posix.dup2(oldFd, newFd));
+        FileDescriptor newFileDescriptor = toDescriptor(newFd);
+
+        new FileOutputStream(toDescriptor(oldFd)).write(outContent);
+        posix.lseek(newFd, SEEK_SET, 0);
+
+        byte[] inContent = new byte[outContent.length];
+        new FileInputStream(newFileDescriptor).read(inContent, 0, 3);
+
+        assertArrayEquals(inContent, outContent);
+    }
+
+    @Test
+    public void fcntlDupfdTest() throws Throwable {
+        if (!Platform.IS_WINDOWS) {
+            File tmp = File.createTempFile("fcntlTest", "tmp");
+            int fd = posix.open(tmp.getPath(), OpenFlags.O_RDWR.intValue(), 0444);
+
+            byte[] outContent = "foo".getBytes();
+
+            // NOTE: This test used to call without the third argument, but this leads to undefined behavior.
+            // See https://github.com/jnr/jnr-posix/issues/144
+            int newFd = posix.fcntl(fd, Fcntl.F_DUPFD, 0);
+
+            new FileOutputStream(JavaLibCHelper.toFileDescriptor(fd)).write(outContent);
+            posix.lseek(fd, SEEK_SET, 0);
+
+            byte[] inContent = new byte[outContent.length];
+            new FileInputStream(JavaLibCHelper.toFileDescriptor(newFd)).read(inContent, 0, 3);
+
+            assertArrayEquals(inContent, outContent);
+        }
+    }
+
+    @Test
+    public void fcntlDupfdWithArgTest() throws Throwable {
+        if (!Platform.IS_WINDOWS) {
+            File tmp = File.createTempFile("dupTest", "tmp");
+            int oldFd = JavaLibCHelper.getfdFromDescriptor(JavaLibCHelper.getDescriptorFromChannel(
+                    new RandomAccessFile(tmp, "rw").getChannel()));
+            int newFd = JavaLibCHelper.getfdFromDescriptor(JavaLibCHelper.getDescriptorFromChannel(
+                    new RandomAccessFile(tmp, "rw").getChannel()));
+
+            byte[] outContent = "foo".getBytes();
+
+            int expectedFd = 100;
+            int dupFd = posix.fcntl(oldFd, Fcntl.F_DUPFD, expectedFd);
+
+            new FileOutputStream(JavaLibCHelper.toFileDescriptor(newFd)).write(outContent);
+
+            byte[] inContent = new byte[outContent.length];
+            new FileInputStream(JavaLibCHelper.toFileDescriptor(dupFd)).read(inContent, 0, 3);
+
+            assertTrue(dupFd >= expectedFd);
+            assertArrayEquals(inContent, outContent);
+        }
+    }
+
+    @Test
+    public void closeTest() throws Throwable {
+        File tmp = File.createTempFile("closeTest", "tmp");
+        int fd = getFdFromDescriptor(JavaLibCHelper.getDescriptorFromChannel(new RandomAccessFile(tmp, "rw").getChannel()));
+
+        int result;
+
+        result = posix.close(fd);
+        assertEquals(0, result);
+
+        result = posix.close(fd);
+        assertEquals(-1, result);
+
+        // TODO (nirvdrum 06-May-15) We're not getting the correct errno value from Windows currently, so we need to skip this test.
+        if (!Platform.IS_WINDOWS) {
+            assertEquals(Errno.EBADF.intValue(), posix.errno());
+        }
+    }
+
+    @Test
+    public void unlinkTestNonWindows() throws Throwable {
+        if (! Platform.IS_WINDOWS) {
+            File tmp = File.createTempFile("unlinkTest", "tmp");
+            RandomAccessFile raf = new RandomAccessFile(tmp, "rw");
+
+            raf.write("hello".getBytes());
+
+            int res = posix.unlink(tmp.getCanonicalPath());
+
+            assertEquals(0, res);
+            assertFalse(tmp.exists());
+
+            raf.write("world".getBytes());
+            raf.seek(0);
+
+            byte[] actual = new byte[10];
+            raf.read(actual);
+
+            assertArrayEquals("helloworld".getBytes(), actual);
+        }
+    }
+
+    @Test
+    public void openTest() throws Throwable {
+        int fd = posix.open("pom.xml", 0, 0666);
+
+        assertNotEquals(-1, fd);
+
+        int result = posix.close(fd);
+        assertEquals(0, result);
+
+        result = posix.close(fd);
+        assertEquals(-1, result);
+
+        fd = posix.open("jnr-posix-filetest.txt", OpenFlags.O_CREAT.intValue() | OpenFlags.O_RDWR.intValue(), 0600);
+
+        assertEquals(0600, posix.stat("jnr-posix-filetest.txt").mode() & 0777);
+
+        result = posix.close(fd);
+        assertEquals(0, result);
+
+        new File("jnr-posix-filetest.txt").delete();
+    }
+
+    @Test
+    public void writeTest() throws Throwable {
+        String str = "To thine own self be true";
+        File tmp = File.createTempFile("writeTest", "tmp");
+        ByteBuffer buffer = ByteBuffer.wrap(str.getBytes());
+
+        int fd = posix.open(tmp.getAbsolutePath(), 1, 066);
+        posix.write(fd, buffer, str.length());
+        posix.close(fd);
+
+        RandomAccessFile raf = new RandomAccessFile(tmp, "rw");
+        assertEquals(raf.readLine(), new String(buffer.array()));
+        posix.unlink(tmp.getAbsolutePath());
+    }
+
+    @Test
+    public void pwriteTest() throws Throwable {
+        if (!Platform.IS_WINDOWS) {
+            String str = "Now is the winter of our discontent";
+            File tmp = File.createTempFile("pwriteTest", "tmp");
+            RandomAccessFile raf = new RandomAccessFile(tmp, "rw");
+            raf.write(str.getBytes());
+            raf.close();
+
+            String str2 = "summer";
+            ByteBuffer buffer = ByteBuffer.wrap(str2.getBytes());
+
+            int fd = posix.open(tmp.getAbsolutePath(), 1, 066);
+            posix.pwrite(fd, buffer, str2.length(), 11);
+            posix.close(fd);
+
+            raf = new RandomAccessFile(tmp, "r");
+            assertEquals(raf.readLine(), "Now is the summer of our discontent");
+            posix.unlink(tmp.getAbsolutePath());
+        }
+    }
+
+    @Test
+    public void truncateTest() throws Throwable {
+        String str = "Beware the Jabberwock, my son!";
+        File tmp = File.createTempFile("truncateTest", "tmp");
+        RandomAccessFile raf = new RandomAccessFile(tmp, "rw");
+        raf.write(str.getBytes());
+        raf.close();
+
+        int shorterLength = str.length() - 10;
+        int longerLength = shorterLength * 2;
+
+        // Truncate should make a file shorter if the new length is less than the old length.
+        posix.truncate(tmp.getAbsolutePath(), shorterLength);
+        assertEquals(shorterLength, tmp.length());
+
+        // Truncate should extend a file if the new length is greater than the old length.
+        posix.truncate(tmp.getAbsolutePath(), longerLength);
+        assertEquals(longerLength, tmp.length());
+    }
+
+    @Test
+    public void ftruncateTest() throws Throwable {
+        String str = "Beware the Jabberwock, my son!";
+        File tmp = File.createTempFile("ftruncateTest", "tmp");
+        RandomAccessFile raf = new RandomAccessFile(tmp, "rw");
+        raf.write(str.getBytes());
+        raf.close();
+
+        int fd = posix.open(tmp.getAbsolutePath(), OpenFlags.O_RDWR.intValue(), 0666);
+        posix.ftruncate(fd, 21);
+        posix.lseekLong(fd, 0, 0);
+        byte[] buf = new byte[21];
+        int read = posix.read(fd, buf, 31);
+        assertEquals(21, read);
+        assertArrayEquals(buf, "Beware the Jabberwock".getBytes());
+    }
+
+    @Test
+    public void fcntlIntTest() throws Throwable {
+        if (!Platform.IS_WINDOWS) {
+            int[] fds = new int[2];
+            int ret = posix.pipe(fds);
+            assertEquals(0, ret);
+            int flags = posix.fcntlInt(fds[0], Fcntl.F_GETFD, 0);
+            posix.fcntlInt(fds[0], Fcntl.F_SETFD, flags | 1); // FD_CLOEXEC
+            assertEquals(flags | 1, posix.fcntlInt(fds[0], Fcntl.F_GETFD, 0));
+        }
+    }
+
+    @Test
+    public void fchmodTest() throws IOException {
+        if (!Platform.IS_WINDOWS) {
+            File tmp = File.createTempFile("jnr-posix-chmod-test", "tmp");
+            int fd = posix.open(tmp.getAbsolutePath(), OpenFlags.O_RDWR.intValue(), 0600);
+
+            assertEquals("chmod: ", 0, posix.fchmod(fd, 0));
+            assertEquals("chmod: ", 0, posix.fchmod(fd, 0777));
+            tmp.delete();
+        }
+    }
+
+
+    @Test
+    public void renameTest() throws IOException {
+        File oldFile = File.createTempFile("jnr-posix-rename-test", "tmp");
+        File newFile = new File(oldFile.getParent() + File.separatorChar + "jnr-posix-rename-test-new");
+
+        assertTrue(oldFile.exists());
+        assertFalse(newFile.exists());
+
+        posix.rename(oldFile.getCanonicalPath(), newFile.getCanonicalPath());
+
+        assertFalse(oldFile.exists());
+        assertTrue(newFile.exists());
+
+        newFile.delete();
+    }
+
+    @Test
+    public void accessTest() throws IOException {
+        File tmp = File.createTempFile("jnr-posix-access-test", "tmp");
+
+        jnr.constants.platform.Access.F_OK.intValue();
+
+        // Set permissions to read-only and verify we don't have permissions to write.
+        String canonicalPath = tmp.getCanonicalPath();
+        assertEquals("chmod 0400: ", 0, posix.chmod(canonicalPath, 0400));
+        assertEquals("access " + canonicalPath + " for write: ", -1, posix.access(canonicalPath, Access.W_OK.intValue()));
+        assertEquals("access " + canonicalPath + " for read: ", 0, posix.access(canonicalPath, Access.R_OK.intValue()));
+
+        // Reset the permissions to read-write and verify we now have permissions to write.
+        assertEquals("chmod 0600: ", 0, posix.chmod(canonicalPath, 0600));
+        assertEquals("access " + canonicalPath + " for write: ", 0, posix.access(canonicalPath, Access.W_OK.intValue()));
+        assertEquals("access " + canonicalPath + " for read: ", 0, posix.access(canonicalPath, Access.R_OK.intValue()));
+
+        // F_OK just checks the file exists and should pass.
+        assertEquals("access " + canonicalPath + " exists: ", 0, posix.access(canonicalPath, Access.F_OK.intValue()));
+    }
+
+    @Test
+    public void readlinkTest() throws IOException {
+        if (!Platform.IS_WINDOWS) {
+            File file = File.createTempFile("jnr-pøsix-réådlînk-tést", "tmp");
+            File link = new File(file.getAbsolutePath() + "link");
+
+            posix.symlink(file.getAbsolutePath(), link.getAbsolutePath());
+
+            byte[] fileBytes = file.getAbsolutePath().getBytes();
+            int fileLength = fileBytes.length;
+
+            byte[] buffer = new byte[fileLength];
+
+            assertEquals(fileLength, posix.readlink(link.getAbsolutePath(), buffer, buffer.length));
+
+            assertArrayEquals(fileBytes, buffer);
+
+            link.delete();
+            file.delete();
+        }
+    }
+
+    @Test
+    public void readlinkByteBufferTest() throws IOException {
+        if (!Platform.IS_WINDOWS) {
+            File file = File.createTempFile("jnr-pøsix-réådlînk-tést", "tmp");
+            File link = new File(file.getAbsolutePath() + "link");
+
+            posix.symlink(file.getAbsolutePath(), link.getAbsolutePath());
+
+            byte[] fileBytes = file.getAbsolutePath().getBytes();
+            int fileLength = fileBytes.length;
+            ByteBuffer buffer = ByteBuffer.allocate(fileLength);
+
+            assertEquals(fileLength, posix.readlink(link.getAbsolutePath(), buffer, buffer.capacity()));
+
+            assertArrayEquals(buffer.array(), file.getAbsolutePath().getBytes());
+
+            link.delete();
+            file.delete();
+        }
+    }
+
+    @Test
+    public void readlinkPointerTest() throws IOException {
+        if (!Platform.IS_WINDOWS) {
+            File file = File.createTempFile("jnr-pøsix-réådlînk-tést", "tmp");
+            File link = new File(file.getAbsolutePath() + "link");
+
+            int bufSize = 1024;
+
+            Pointer buffer = jnr.ffi.Runtime.getSystemRuntime().getMemoryManager().allocateDirect(bufSize);
+
+            posix.symlink(file.getAbsolutePath(), link.getAbsolutePath());
+            
+            byte[] fileBytes = file.getAbsolutePath().getBytes();
+            int fileLength = fileBytes.length;
+
+            assertEquals(fileLength, posix.readlink(link.getAbsolutePath(), buffer, bufSize));
+
+            assertEquals(file.getAbsolutePath(), buffer.getString(0, fileLength, Charset.defaultCharset()));
+
+            link.delete();
+            file.delete();
+        }
+    }
+
+    @Test
+    public void mkfifoTest() throws Throwable {
+        if (!Platform.IS_WINDOWS) {
+            File tmp = File.createTempFile("mkfifoTest", "tmp");
+            tmp.deleteOnExit();
+
+            int ret = posix.mkfifo(tmp.getAbsolutePath(), 0666);
+
+            FileInputStream fis = new FileInputStream(tmp);
+            FileOutputStream fos = new FileOutputStream(tmp);
+
+            byte[] content = "hello".getBytes();
+
+            fos.write(content);
+            fis.read(content);
+
+            assertArrayEquals("hello".getBytes(), content);
+        }
+    }
+
+    @Test
+    public void lseekTest() throws Throwable {
+        if (Platform.IS_MAC) {
+            int fd = posix.open("/dev/zero", 0, 0);
+
+            // use 2^33 to ensure we're out of int range
+            long offset = (long) Math.pow(2, 33);
+            long seek = posix.lseekLong(fd, offset, 0);
+
+            assertEquals(seek, offset);
+        }
+    }
+
+    private int getFdFromDescriptor(FileDescriptor descriptor) {
+        if (Platform.IS_WINDOWS) {
+            HANDLE handle = JavaLibCHelper.gethandle(descriptor);
+            return ((WindowsLibC) posix.libc())._open_osfhandle(handle, 0);
+        } else {
+            return JavaLibCHelper.getfdFromDescriptor(descriptor);
+        }
+    }
+
+    private FileDescriptor toDescriptor(int fd) {
+        if (Platform.IS_WINDOWS) {
+            HANDLE handle = ((WindowsLibC) posix.libc())._get_osfhandle(fd);
+            return JavaLibCHelper.toFileDescriptor(handle);
+        } else {
+            return JavaLibCHelper.toFileDescriptor(fd);
+        }
+    }
+
+    private boolean hasNanosecondPrecision(FileStat fileStat) {
+        return fileStat instanceof NanosecondFileStat && !Platform.IS_MAC;
+    }
+
+    private void utimensat(File file, int parentFd) {
+        String path = file.getAbsolutePath();
+        FileStat fileStat = posix.stat(path);
+        if (!hasNanosecondPrecision(fileStat)) {
+            file.delete();
+            return;
+        }
+
+        long atimeSeconds = fileStat.atime()+2;
+        long mtimeSeconds = fileStat.mtime()-2;
+        long atimeNanoSeconds = 123456789;
+        long mtimeNanoSeconds = 135;
+        posix.utimensat(parentFd,
+                path,
+                new long[] {atimeSeconds, atimeNanoSeconds},
+                new long[] {mtimeSeconds, mtimeNanoSeconds},
+                0);
+        assertStatNanoSecond(file, atimeSeconds, atimeNanoSeconds, mtimeSeconds, mtimeNanoSeconds);
+    }
+
+    private void assertStatNanoSecond(File file, long atimeSeconds, long atimeNanoSeconds, long mtimeSeconds, long mtimeNanoSeconds) {
+        NanosecondFileStat nanosecondFileStat = (NanosecondFileStat) posix.stat(file.getPath());
+        assertEquals("Access timestamp should be updated", atimeSeconds, nanosecondFileStat.atime());
+        assertEquals("Modification timestamp should be updated", mtimeSeconds, nanosecondFileStat.mtime());
+        assertEquals("Access time precision should be in ns", atimeNanoSeconds, nanosecondFileStat.aTimeNanoSecs());
+        assertEquals("Modification time precision should be in ns", mtimeNanoSeconds, nanosecondFileStat.mTimeNanoSecs());
+        file.delete();
+    }
+}
diff --git a/src/test/java/jnr/posix/GroupTest.java b/src/test/java/jnr/posix/GroupTest.java
new file mode 100644
index 0000000..6ede36a
--- /dev/null
+++ b/src/test/java/jnr/posix/GroupTest.java
@@ -0,0 +1,149 @@
+
+package jnr.posix;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import jnr.constants.platform.LangInfo;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ */
+public class GroupTest {
+
+    public GroupTest() {
+    }
+
+    private static POSIX posix;
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        posix = POSIXFactory.getPOSIX(new DummyPOSIXHandler(), true);
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+    }
+
+    @Before
+    public void setUp() {
+    }
+
+    @After
+    public void tearDown() {
+    }
+
+    // TODO add test methods here.
+    // The methods must be annotated with annotation @Test. For example:
+    //
+    // @Test
+    // public void hello() {}
+    @Test
+    public void getgrnam() {
+        if (jnr.ffi.Platform.getNativePlatform().isUnix()) {
+            final String LOGIN = "daemon";
+            Group grp = posix.getgrnam(LOGIN);
+            assertNotNull(grp);
+            assertEquals("Login name not equal", LOGIN, grp.getName());
+        }
+    }
+
+    @Test
+    public void nonExistantGroupReturnsNull() {
+        if (jnr.ffi.Platform.getNativePlatform().isUnix()) {
+            final String LOGIN = "dkjhfjkdsfhjksdhfsdjkhfsdkjhfdskj";
+            assertNull("getpwnam for non-existant group should return null", posix.getgrnam(LOGIN));
+        }
+    }
+
+    @Test
+    public void getgrent() {
+        if (jnr.ffi.Platform.getNativePlatform().isUnix()) {
+            ArrayList<Group> grps = new ArrayList<Group>();
+            while (true) {
+                Group grp = posix.getgrent();
+                if (grp == null) {
+                    break;
+                }
+                grps.add(grp);
+            }
+            for (Group grp : grps) {
+                assertNotNull(grp.getName());
+                assertNotNull(grp.getPassword());
+                assertNotNull(grp.getGID());
+                for (String member : grp.getMembers()) {
+                    assertNotNull(member);
+                }
+            }
+        }
+    }
+
+    @Test
+    public void getgroups() throws Throwable {
+        if (jnr.ffi.Platform.getNativePlatform().isUnix()) {
+            String[] groupIdsAsStrings = exec("id", "-G").split(" ");
+            long[] expectedGroupIds = new long[groupIdsAsStrings.length];
+
+            for (int i = 0; i < groupIdsAsStrings.length; i++) {
+                expectedGroupIds[i] = Long.parseLong(groupIdsAsStrings[i]);
+            }
+
+            long[] actualGroupIds = posix.getgroups();
+
+            // getgroups does not specify whether the effective group ID is included along with the supplementary
+            // group IDs. However, `id -G` always includes all group IDs. So, we must do something of a fuzzy match.
+            // If the actual list is shorter than the expected list by 1, alter the expected list by removing the
+            // effective group ID before comparing the two arrays.
+            if (actualGroupIds.length == expectedGroupIds.length - 1) {
+                long effectiveGroupId = Long.parseLong(exec("id", "-g"));
+                expectedGroupIds = removeElement(expectedGroupIds, effectiveGroupId);
+            }
+
+            Arrays.sort(expectedGroupIds);
+            Arrays.sort(actualGroupIds);
+
+            assertArrayEquals(expectedGroupIds, actualGroupIds);
+        }
+    }
+
+    private String exec(String... command) throws IOException {
+        InputStreamReader isr = null;
+        BufferedReader reader = null;
+
+        try {
+            isr = new InputStreamReader(Runtime.getRuntime().exec(command).getInputStream());
+            reader = new BufferedReader(isr);
+
+            return reader.readLine();
+        } finally {
+            if (reader != null) {
+                reader.close();
+            }
+
+            if (isr != null) {
+                isr.close();
+            }
+        }
+    }
+
+    private long[] removeElement(long[] array, long value) {
+        for (int i = 0; i < array.length; i++) {
+            if (array[i] == value) {
+                long[] ret = new long[array.length - 1];
+                System.arraycopy(array, 0, ret, 0, i);
+                System.arraycopy(array, i + 1, ret, i, array.length - i - 1);
+                return ret;
+            }
+        }
+
+        return array;
+    }
+}
diff --git a/src/test/java/jnr/posix/HANDLETest.java b/src/test/java/jnr/posix/HANDLETest.java
new file mode 100644
index 0000000..eceeadd
--- /dev/null
+++ b/src/test/java/jnr/posix/HANDLETest.java
@@ -0,0 +1,57 @@
+package jnr.posix;
+
+import jnr.posix.util.Platform;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class HANDLETest {
+
+    private static POSIX posix;
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        posix = POSIXFactory.getPOSIX(new DummyPOSIXHandler(), true);
+    }
+
+    @Test
+    public void invalidHandleIsInValid() {
+        assertFalse(HANDLE.valueOf(-1L).isValid());
+    }
+
+    private static WindowsLibC kernel32() {
+        return (WindowsLibC) posix.libc();
+    }
+    
+    // This really is matching pipe for the sake of these unit tests but in general the 
+    // in/out/error handle will vary depending on how a CLI supplies it: 
+    // http://stackoverflow.com/questions/9021916/how-do-i-check-if-my-delphi-console-app-is-redirected-to-a-file-or-pipe
+    private boolean isValidHandleType(HANDLE handle) {
+        int type = kernel32().GetFileType(handle);
+        
+        return type == WindowsLibC.FILE_TYPE_DISK || type == WindowsLibC.FILE_TYPE_PIPE;
+    }
+
+    @Test
+    public void stdinHandle() {
+        if (Platform.IS_WINDOWS) {
+            assertTrue(kernel32().GetStdHandle(WindowsLibC.STD_INPUT_HANDLE).isValid());
+            assertTrue(isValidHandleType(kernel32().GetStdHandle(WindowsLibC.STD_INPUT_HANDLE)));
+        }
+    }
+
+    @Test
+    public void stdoutHandle() {
+        if (Platform.IS_WINDOWS) {
+            assertTrue(kernel32().GetStdHandle(WindowsLibC.STD_OUTPUT_HANDLE).isValid());
+            assertTrue(isValidHandleType(kernel32().GetStdHandle(WindowsLibC.STD_OUTPUT_HANDLE)));
+        }
+    }
+
+    @Test
+    public void stderrHandle() {
+        if (Platform.IS_WINDOWS) {
+            assertTrue(kernel32().GetStdHandle(WindowsLibC.STD_ERROR_HANDLE).isValid());
+            assertTrue(isValidHandleType(kernel32().GetStdHandle(WindowsLibC.STD_ERROR_HANDLE)));
+        }
+    }
+}
diff --git a/src/test/java/jnr/posix/HostnameTest.java b/src/test/java/jnr/posix/HostnameTest.java
new file mode 100644
index 0000000..cb094ce
--- /dev/null
+++ b/src/test/java/jnr/posix/HostnameTest.java
@@ -0,0 +1,47 @@
+package jnr.posix;
+
+import org.hamcrest.Matcher;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+import static org.junit.Assume.*;
+
+import java.io.IOException;
+import java.util.Scanner;
+
+public class HostnameTest {
+    private static POSIX posix;
+    private static POSIX jPosix;
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        posix = POSIXFactory.getPOSIX(new DummyPOSIXHandler(), true);
+        jPosix = POSIXFactory.getJavaPOSIX();
+    }
+
+    @Test
+    public void testHostnameWorks() {
+        assertNotNull(posix.gethostname());
+    }
+    
+    @Test
+    public void jPosixIsReasonable() {
+        // cast here works around a generic resolution problem in Java 11+
+        assumeThat(System.getenv().keySet(), (Matcher) anyOf(hasItem("HOSTNAME"), hasItem("COMPUTERNAME")));
+        assertNotNull(jPosix.gethostname());
+    }
+
+    @Test
+    public void testHostnameIsResonable() throws IOException {
+        String hostname = "";
+        try {
+            hostname = new Scanner(Runtime.getRuntime().exec("hostname").getInputStream()).next();
+        } catch (IOException e) {
+            assumeNoException(e);
+        }
+        assumeThat(hostname, is(not(equalTo(""))));
+        assertThat(posix.gethostname().toLowerCase(), equalTo(hostname.toLowerCase()));
+    }
+}
diff --git a/src/test/java/jnr/posix/IDTest.java b/src/test/java/jnr/posix/IDTest.java
new file mode 100644
index 0000000..c98c2e6
--- /dev/null
+++ b/src/test/java/jnr/posix/IDTest.java
@@ -0,0 +1,70 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: EPL 2.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Eclipse Public
+ * License Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+/**
+ * $Id: $
+ */
+
+package jnr.posix;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class IDTest {
+
+    public IDTest() {
+    }
+    private static POSIX nativePosix, javaPosix;
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        nativePosix = POSIXFactory.getPOSIX(new DummyPOSIXHandler(), true);
+        javaPosix = POSIXFactory.getPOSIX(new DummyPOSIXHandler(), false);
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+    }
+
+    @Before
+    public void setUp() {
+    }
+
+    @After
+    public void tearDown() {
+    }
+
+    @Test public void getuid() {
+        assertEquals("Java posix did not return same value as native posix", nativePosix.getuid(), javaPosix.getuid());
+    }
+    @Test public void getgid() {
+        assertEquals("Java posix did not return same value as native posix", nativePosix.getgid(), javaPosix.getgid());
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/jnr/posix/IOTest.java b/src/test/java/jnr/posix/IOTest.java
new file mode 100644
index 0000000..b0c0c98
--- /dev/null
+++ b/src/test/java/jnr/posix/IOTest.java
@@ -0,0 +1,231 @@
+package jnr.posix;
+
+import jnr.constants.platform.*;
+import jnr.posix.util.Platform;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.File;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.IntBuffer;
+
+/**
+ * Created by headius on 5/31/14.
+ */
+public class IOTest {
+    private static POSIX posix;
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        posix = POSIXFactory.getPOSIX(new DummyPOSIXHandler(), true);
+    }
+
+    @Test
+    public void testOpenReadWrite() throws Throwable {
+        if (!Platform.IS_WINDOWS) {
+            File tmp = File.createTempFile("IOTest", "testOpen");
+            int fd = posix.open(tmp.getPath(), OpenFlags.O_RDWR.intValue(), 0666);
+            Assert.assertTrue(fd > 0);
+
+            byte[] hello = "hello".getBytes();
+            int written = posix.write(fd, hello, 5);
+            Assert.assertEquals(5, written);
+
+            byte[] buf = new byte[5];
+            posix.lseekLong(fd, 0, 0); // no jnr-constants for SEEK_SET
+            int read = posix.read(fd, buf, 5);
+            Assert.assertEquals(5, read);
+            Assert.assertArrayEquals(hello, buf);
+
+            byte[] goodbye = "goodbye".getBytes();
+            written = posix.pwrite(fd, goodbye, 7, 3);
+            Assert.assertEquals(7, written);
+            Assert.assertEquals(5, posix.lseekLong(fd, 0, 1)); // SEEK_CUR
+
+            byte[] bye = new byte[3];
+            read = posix.pread(fd, bye, 3, 7);
+            Assert.assertEquals(3, read);
+            Assert.assertEquals(5, posix.lseekLong(fd, 0, 1)); // SEEK_CUR
+            Assert.assertArrayEquals("bye".getBytes(), bye);
+        }
+    }
+
+    @Test
+    public void testPipe() throws Throwable {
+        int[] fds = {0, 0};
+        int ret = posix.pipe(fds);
+        Assert.assertTrue(ret >= 0);
+        Assert.assertTrue(fds[0] > 0);
+        Assert.assertTrue(fds[1] > 0);
+
+        byte[] hello = "hello".getBytes();
+        int written = posix.write(fds[1], hello, 5);
+        Assert.assertEquals(5, written);
+
+        byte[] buf = new byte[5];
+        int read = posix.read(fds[0], buf, 5);
+        Assert.assertEquals(5, read);
+        Assert.assertArrayEquals(buf, hello);
+    }
+
+    @Test
+    public void testPathconf() {
+        if (Platform.IS_WINDOWS) {
+            int res = posix.fpathconf(-1, Pathconf._PC_PIPE_BUF);
+            Assert.assertEquals(-1, res);
+            Assert.assertEquals(Errno.EOPNOTSUPP.intValue(), posix.errno());
+        } else {
+            int res = posix.fpathconf(-1, Pathconf._PC_PIPE_BUF);
+            Assert.assertEquals(-1, res);
+            
+            int[] fds = {0, 0};
+            posix.pipe(fds);
+            res = posix.fpathconf(fds[1], Pathconf._PC_PIPE_BUF);
+            if (Platform.IS_LINUX) {
+                Assert.assertEquals(4096, res);
+            } else {
+                Assert.assertTrue(res > 0);
+            }
+        }
+    }
+
+    @Test
+    public void testSocketPair() throws Throwable {
+        if (!Platform.IS_WINDOWS) {
+            int[] fds = {0, 0};
+
+            int ret = posix.socketpair(AddressFamily.AF_UNIX.intValue(), Sock.SOCK_STREAM.intValue(), 0, fds);
+
+            Assert.assertTrue(ret >= 0);
+            Assert.assertTrue(fds[0] > 0);
+            Assert.assertTrue(fds[1] > 0);
+
+            byte[] hello = "hello".getBytes();
+            int written = posix.write(fds[1], hello, 5);
+            Assert.assertEquals(5, written);
+
+            byte[] buf = new byte[5];
+            int read = posix.read(fds[0], buf, 5);
+            Assert.assertEquals(5, read);
+            Assert.assertArrayEquals(buf, hello);
+
+            hello = "goodbye".getBytes();
+            written = posix.write(fds[0], hello, 7);
+            Assert.assertEquals(7, written);
+
+            buf = new byte[7];
+            read = posix.read(fds[1], buf, 7);
+            Assert.assertEquals(7, read);
+            Assert.assertArrayEquals(buf, hello);
+        }
+    }
+
+    @Test
+    public void testSendRecvMsg_NoControl() throws Throwable {
+        if (!Platform.IS_WINDOWS) {
+            int[] fds = {0, 0};
+
+            int ret = posix.socketpair(AddressFamily.AF_UNIX.intValue(), Sock.SOCK_STREAM.intValue(), 0, fds);
+
+            Assert.assertTrue(ret >= 0);
+            Assert.assertTrue(fds[0] > 0);
+            Assert.assertTrue(fds[1] > 0);
+
+            MsgHdr outMessage = posix.allocateMsgHdr();
+
+            String data = "does this work?";
+            byte[] dataBytes = data.getBytes();
+
+            ByteBuffer[] outIov = new ByteBuffer[1];
+            outIov[0] = ByteBuffer.allocateDirect(dataBytes.length);
+            outIov[0].put(dataBytes);
+            outIov[0].flip();
+
+            outMessage.setIov(outIov);
+
+            int sendStatus = posix.sendmsg(fds[0], outMessage, 0);
+
+            Assert.assertTrue(sendStatus == dataBytes.length);
+
+            // ----------------
+
+            MsgHdr inMessage = posix.allocateMsgHdr();
+            ByteBuffer[] inIov = new ByteBuffer[1];
+            inIov[0] = ByteBuffer.allocateDirect(1024);
+            inMessage.setIov(inIov);
+
+            int recvStatus = posix.recvmsg(fds[1], inMessage, 0);
+
+            Assert.assertTrue(recvStatus == dataBytes.length);
+
+            for (int i = 0; i < recvStatus; ++i) {
+                Assert.assertEquals(dataBytes[i], outIov[0].get(i));
+            }
+        }
+    }
+
+    @Test
+    public void testSendRecvMsg_WithControl() throws Throwable {
+        if (!Platform.IS_WINDOWS) {
+            int[] fds = {0, 0};
+
+            int ret = posix.socketpair(AddressFamily.AF_UNIX.intValue(), Sock.SOCK_STREAM.intValue(), 0, fds);
+
+            String data = "does this work?";
+            byte[] dataBytes = data.getBytes();
+
+
+            Assert.assertTrue(ret >= 0);
+            Assert.assertTrue(fds[0] > 0);
+            Assert.assertTrue(fds[1] > 0);
+
+            MsgHdr outMessage = posix.allocateMsgHdr();
+
+            ByteBuffer[] outIov = new ByteBuffer[1];
+            outIov[0] = ByteBuffer.allocateDirect(dataBytes.length);
+            outIov[0].put(dataBytes);
+            outIov[0].flip();
+
+            outMessage.setIov(outIov);
+
+            CmsgHdr outControl = outMessage.allocateControl(4);
+            outControl.setLevel(SocketLevel.SOL_SOCKET.intValue());
+            outControl.setType(0x01);
+
+            ByteBuffer fdBuf = ByteBuffer.allocateDirect(4);
+            fdBuf.order(ByteOrder.nativeOrder());
+            fdBuf.putInt(0, fds[0]);
+            outControl.setData(fdBuf);
+
+            int sendStatus = posix.sendmsg(fds[0], outMessage, 0);
+
+            Assert.assertTrue(sendStatus == dataBytes.length);
+
+            // ----------------
+
+            MsgHdr inMessage = posix.allocateMsgHdr();
+            ByteBuffer[] inIov = new ByteBuffer[1];
+            inIov[0] = ByteBuffer.allocateDirect(1024);
+            inMessage.setIov(inIov);
+
+            inMessage.allocateControl(4);
+            int recvStatus = posix.recvmsg(fds[1], inMessage, 0);
+
+            Assert.assertTrue(recvStatus == dataBytes.length);
+
+            ByteBuffer inFdBuf = inMessage.getControls()[0].getData();
+            inFdBuf.order(ByteOrder.nativeOrder());
+
+            int fd = inFdBuf.getInt();
+
+            Assert.assertTrue(fd != 0);
+
+            for (int i = 0; i < recvStatus; ++i) {
+                Assert.assertEquals(dataBytes[i], outIov[0].get(i));
+            }
+
+        }
+    }
+}
diff --git a/src/test/java/jnr/posix/JavaFileStatTest.java b/src/test/java/jnr/posix/JavaFileStatTest.java
new file mode 100644
index 0000000..88ebc05
--- /dev/null
+++ b/src/test/java/jnr/posix/JavaFileStatTest.java
@@ -0,0 +1,27 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package jnr.posix;
+
+import jnr.posix.JavaFileStat;
+import junit.framework.TestCase;
+
+import jnr.posix.util.Platform;
+
+/**
+ *
+ * @author nicksieger
+ */
+public class JavaFileStatTest extends TestCase {
+    public void testSetup() {
+        JavaFileStat fs = new JavaFileStat(null, null);
+        if (Platform.IS_WINDOWS) {
+            fs.setup("c:/");
+        } else {
+            fs.setup("/");
+        }
+        assertFalse(fs.isSymlink());
+    }
+}
diff --git a/src/test/java/jnr/posix/JavaPOSIXTest.java b/src/test/java/jnr/posix/JavaPOSIXTest.java
new file mode 100644
index 0000000..c5b93fd
--- /dev/null
+++ b/src/test/java/jnr/posix/JavaPOSIXTest.java
@@ -0,0 +1,119 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package jnr.posix;
+
+import java.io.File;
+import java.io.IOException;
+
+import jnr.constants.platform.Errno;
+import jnr.posix.JavaPOSIX;
+import jnr.posix.POSIX;
+import jnr.posix.Passwd;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author wayne
+ */
+public class JavaPOSIXTest {
+    POSIX posix;
+    public JavaPOSIXTest() {
+    }
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+    }
+
+    @Before
+    public void setUp() {
+        posix = new JavaPOSIX(new DummyPOSIXHandler());
+    }
+
+    @After
+    public void tearDown() {
+        posix = null;
+    }
+
+    // TODO add test methods here.
+    // The methods must be annotated with annotation @Test. For example:
+    //
+    // @Test
+    // public void hello() {}
+    @Test public void uid() {
+        
+    }
+
+    @Test public void lstatTest() throws IOException {
+        File file = createTestFile();
+
+        FileStat stat = posix.lstat(file.getName());
+
+        assertTrue(stat.isEmpty());
+
+        stat = posix.allocateStat();
+        int ret = posix.lstat(file.getPath(), stat);
+
+        assertTrue(ret >= 0);
+        assertTrue(stat.isEmpty());
+
+        file.delete();
+
+        stat = posix.allocateStat();
+        ret = posix.lstat(file.getPath(), stat);
+
+        assertTrue(ret < 0);
+    }
+
+    @Test public void statMissingFileTest() throws IOException {
+        FileStat stat = posix.allocateStat();
+
+        int ret = posix.stat("does not exist", stat);
+
+        assertEquals(-1, ret);
+        assertEquals(Errno.ENOENT.intValue(), posix.errno());
+    }
+
+	@Test
+	public void chmodTest() throws IOException {
+        File file = createTestFile();
+
+		assertEquals("chmod: ", 0, posix.chmod(file.getName(), 0));
+		assertEquals("chmod: ", 0, posix.chmod(file.getName(), 0777));
+
+		file.delete();
+	}
+
+    @Test public void getpwuid() {
+        Passwd pwd = posix.getpwuid(posix.getuid());
+        assertNotNull("getpwuid failed", pwd);
+        
+    }
+    @Test public void isNative() {
+        assertFalse("JavaPOSIX isNative should be false", posix.isNative());
+    }
+
+    private File createTestFile() throws IOException {
+        // create a tmp file
+        String fName = "test.dat";
+        File file = new File(fName);
+        file.createNewFile();
+
+        return file;
+    }
+
+    @Test public void getpid() {
+        assertEquals(POSIXFactory.getNativePOSIX().getpid(), posix.getpid());
+    }
+}
diff --git a/src/test/java/jnr/posix/LinuxPOSIXTest.java b/src/test/java/jnr/posix/LinuxPOSIXTest.java
new file mode 100644
index 0000000..bdec625
--- /dev/null
+++ b/src/test/java/jnr/posix/LinuxPOSIXTest.java
@@ -0,0 +1,199 @@
+package jnr.posix;
+
+import jnr.constants.platform.AddressFamily;
+import jnr.constants.platform.PosixFadvise;
+import jnr.constants.platform.OpenFlags;
+import jnr.constants.platform.Sock;
+import jnr.constants.platform.SocketLevel;
+import jnr.constants.platform.SocketOption;
+import jnr.ffi.Platform;
+import jnr.posix.util.ConditionalTestRule;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.io.File;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.RunnableFuture;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.locks.Lock;
+
+import static jnr.posix.LinuxIoPrio.*;
+
+public class LinuxPOSIXTest {
+
+    @ClassRule
+    public static RunningOnLinux rule = new RunningOnLinux();
+
+    private static Linux linuxPOSIX = null;
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        POSIX posix = POSIXFactory.getNativePOSIX();
+
+        if (posix instanceof Linux) {
+            linuxPOSIX = (Linux) posix;
+        }
+    }
+
+    /**
+     * Tests that IO priority can be set to a thread and that it doesn't change priority set on
+     * another thread
+     * @throws InterruptedException
+     * @throws ExecutionException
+     */
+    @Test
+    public void ioprioThreadedTest() throws InterruptedException, ExecutionException {
+        linuxPOSIX.ioprio_set(IOPRIO_WHO_PROCESS, 0, IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 4));
+
+        Future<Integer> threadPriorityFuture = Executors.newFixedThreadPool(1).submit(new Callable<Integer>() {
+            @Override
+            public Integer call() throws Exception {
+                linuxPOSIX.ioprio_set(IOPRIO_WHO_PROCESS, 0, IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 7));
+                return linuxPOSIX.ioprio_get(IOPRIO_WHO_PROCESS, 0);
+            }
+        });
+
+        int threadPriority = threadPriorityFuture.get().intValue();
+
+        Assert.assertEquals(7, IOPRIO_PRIO_DATA(threadPriority));
+        Assert.assertEquals(IOPRIO_CLASS_IDLE, IOPRIO_PRIO_CLASS(threadPriority));
+        Assert.assertEquals(4, IOPRIO_PRIO_DATA(linuxPOSIX.ioprio_get(IOPRIO_WHO_PROCESS, 0)));
+        Assert.assertEquals(IOPRIO_CLASS_BE, IOPRIO_PRIO_CLASS(linuxPOSIX.ioprio_get(IOPRIO_WHO_PROCESS, 0)));
+    }
+
+    @Test
+    public void testMessageHdrMultipleControl() {
+        if (jnr.posix.util.Platform.IS_WINDOWS) {
+            return;
+        }
+
+        int[] fds = {0, 0};
+
+        int ret = linuxPOSIX.socketpair(AddressFamily.AF_UNIX.intValue(),
+                Sock.SOCK_STREAM.intValue(),
+                0,
+                fds);
+
+
+        String data = "twoControlMessages";
+        byte[] dataBytes = data.getBytes();
+
+        Assert.assertTrue(ret >= 0);
+        Assert.assertTrue(fds[0] > 0);
+        Assert.assertTrue(fds[1] > 0);
+
+        ByteBuffer buf = ByteBuffer.allocate(4);
+        buf.order(ByteOrder.nativeOrder());
+        buf.putInt(1).flip();
+
+        ret = linuxPOSIX.libc().setsockopt(fds[1],
+                SocketLevel.SOL_SOCKET.intValue(),
+                SocketOption.SO_PASSCRED.intValue(),
+                buf,
+                buf.remaining());
+
+        Assert.assertTrue(ret >= 0);
+
+        MsgHdr outMessage = linuxPOSIX.allocateMsgHdr();
+
+        ByteBuffer[] outIov = new ByteBuffer[1];
+        outIov[0] = ByteBuffer.allocateDirect(dataBytes.length);
+        outIov[0].put(dataBytes);
+        outIov[0].flip();
+
+        outMessage.setIov(outIov);
+
+        CmsgHdr[] outControl = outMessage.allocateControls(new int[]{4});
+        outControl[0].setLevel(SocketLevel.SOL_SOCKET.intValue());
+        outControl[0].setType(0x01);
+
+        ByteBuffer fdBuf = ByteBuffer.allocateDirect(4);
+        fdBuf.order(ByteOrder.nativeOrder());
+        fdBuf.putInt(0, fds[0]);
+        outControl[0].setData(fdBuf);
+
+        int sendStatus = linuxPOSIX.sendmsg(fds[0], outMessage, 0);
+        if (sendStatus == -1) {
+            String sendmsgError = "Error with sendmsg: " + linuxPOSIX.strerror(linuxPOSIX.errno());
+            Assert.fail(sendmsgError);
+            return;
+        }
+
+        Assert.assertEquals(dataBytes.length, sendStatus);
+
+        // ----------------
+
+        MsgHdr inMessage = linuxPOSIX.allocateMsgHdr();
+        ByteBuffer[] inIov = new ByteBuffer[1];
+        inIov[0] = ByteBuffer.allocateDirect(1024);
+        inMessage.setIov(inIov);
+
+        inMessage.allocateControls(new int[]{4, 12});
+        int recvStatus = linuxPOSIX.recvmsg(fds[1], inMessage, 0);
+
+        Assert.assertEquals(dataBytes.length, recvStatus);
+
+        Assert.assertEquals(2, inMessage.getControls().length);
+
+        CmsgHdr[] controls = inMessage.getControls();
+        for (int x = 0; x < controls.length; x++) {
+            validateCmsghdr(controls[x]);
+        }
+    }
+
+    private void validateCmsghdr(CmsgHdr control) {
+        if (control.getLevel() == SocketLevel.SOL_SOCKET.intValue()
+                && control.getType() == 0x01) {
+            // Passing a FD
+            ByteBuffer inFdBuf = control.getData();
+            inFdBuf.order(ByteOrder.nativeOrder());
+
+            int fd = inFdBuf.getInt();
+
+            Assert.assertTrue(fd != 0);
+        } else if (control.getLevel() == SocketLevel.SOL_SOCKET.intValue()
+                && control.getType() == 0x02) {
+            //Credentials
+            ByteBuffer data = control.getData();
+            data.order(ByteOrder.nativeOrder());
+
+            int got_pid = data.getInt();
+            int got_uid = data.getInt();
+            int got_gid = data.getInt();
+
+            Assert.assertEquals(linuxPOSIX.getpid(), got_pid);
+            Assert.assertEquals(linuxPOSIX.getuid(), got_uid);
+            Assert.assertEquals(linuxPOSIX.getgid(), got_gid);
+        } else {
+            Assert.fail("Unable to determine cmsghdr type");
+        }
+    }
+
+    @Test
+    public void testPosixFadvise() throws Throwable {
+        File file = File.createTempFile("posix_fadvise", null);
+        int fd = linuxPOSIX.open(file.getAbsolutePath(), OpenFlags.O_RDWR.intValue(), 0444);
+
+        Assert.assertEquals(0, linuxPOSIX.posix_fadvise(fd, 0, 0, PosixFadvise.POSIX_FADV_SEQUENTIAL));
+        Assert.assertEquals(0, linuxPOSIX.posix_fadvise(fd, 0, 0, PosixFadvise.POSIX_FADV_RANDOM));
+        Assert.assertEquals(0, linuxPOSIX.posix_fadvise(fd, 0, 0, PosixFadvise.POSIX_FADV_NOREUSE));
+        Assert.assertEquals(0, linuxPOSIX.posix_fadvise(fd, 0, 0, PosixFadvise.POSIX_FADV_WILLNEED));
+        Assert.assertEquals(0, linuxPOSIX.posix_fadvise(fd, 0, 0, PosixFadvise.POSIX_FADV_DONTNEED));
+        Assert.assertEquals(0, linuxPOSIX.posix_fadvise(fd, 0, 0, PosixFadvise.POSIX_FADV_NORMAL)); // normal last to reset it
+
+        linuxPOSIX.close(fd);
+    }
+}
+
+class RunningOnLinux extends ConditionalTestRule {
+    public boolean isSatisfied() {
+        return jnr.ffi.Platform.getNativePlatform().getOS().equals(Platform.OS.LINUX);
+    }
+}
diff --git a/src/test/java/jnr/posix/NlLanginfoTest.java b/src/test/java/jnr/posix/NlLanginfoTest.java
new file mode 100644
index 0000000..2c13742
--- /dev/null
+++ b/src/test/java/jnr/posix/NlLanginfoTest.java
@@ -0,0 +1,45 @@
+package jnr.posix;
+
+import jnr.constants.platform.LangInfo;
+import jnr.posix.util.Platform;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+import static org.junit.Assert.assertEquals;
+
+public class NlLanginfoTest {
+    private static POSIX posix;
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        posix = POSIXFactory.getPOSIX(new DummyPOSIXHandler(), true);
+    }
+
+    @Test
+    public void testNlLanginfo() throws Throwable {
+        if (!Platform.IS_WINDOWS) {
+            InputStreamReader isr = null;
+            BufferedReader reader = null;
+
+            try {
+                isr = new InputStreamReader(Runtime.getRuntime().exec("locale charmap").getInputStream());
+                reader = new BufferedReader(isr);
+
+                String localeCharmap = reader.readLine();
+                assertEquals(localeCharmap, posix.nl_langinfo(LangInfo.CODESET.intValue()));
+            } finally {
+                if (reader != null) {
+                    reader.close();
+                }
+
+                if (isr != null) {
+                    isr.close();
+                }
+            }
+        }
+    }
+
+}
diff --git a/src/test/java/jnr/posix/POSIXFactoryTest.java b/src/test/java/jnr/posix/POSIXFactoryTest.java
new file mode 100644
index 0000000..c2ea3e7
--- /dev/null
+++ b/src/test/java/jnr/posix/POSIXFactoryTest.java
@@ -0,0 +1,20 @@
+package jnr.posix;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class POSIXFactoryTest {
+    @Test
+    public void testGetNativePOSIX() throws Throwable {
+        POSIX posix = POSIXFactory.getNativePOSIX();
+        assertTrue(posix.isNative());
+    }
+
+    @Test
+    public void testGetJavaPOSIX() throws Throwable {
+        POSIX posix = POSIXFactory.getJavaPOSIX();
+        assertFalse(posix.isNative());
+    }
+}
diff --git a/src/test/java/jnr/posix/PasswdTest.java b/src/test/java/jnr/posix/PasswdTest.java
new file mode 100644
index 0000000..623a7bf
--- /dev/null
+++ b/src/test/java/jnr/posix/PasswdTest.java
@@ -0,0 +1,69 @@
+
+package jnr.posix;
+
+import jnr.posix.util.Platform;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ */
+public class PasswdTest {
+
+    public PasswdTest() {
+    }
+    private static POSIX posix;
+    private static Class passwdClass;
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        if (Platform.IS_MAC) {
+            passwdClass = MacOSPasswd.class;
+        } else if (Platform.IS_LINUX) {
+            passwdClass = LinuxPasswd.class;
+        } else if (Platform.IS_FREEBSD) {
+            passwdClass = FreeBSDPasswd.class;
+        } else if (Platform.IS_OPENBSD) {
+            passwdClass = OpenBSDPasswd.class;
+        } else if (Platform.IS_SOLARIS) {
+            passwdClass = SolarisPasswd.class;
+        } else if (Platform.IS_WINDOWS) {
+            // Skipping these tests for windows
+        } else {
+            throw new IllegalArgumentException("Platform not supported");
+        }
+        posix = POSIXFactory.getPOSIX(new DummyPOSIXHandler(), true);
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+    }
+
+    @Before
+    public void setUp() {
+    }
+
+    @After
+    public void tearDown() {
+    }
+
+    @Test public void getpwnam() {
+        if (jnr.ffi.Platform.getNativePlatform().isUnix()) {
+            final String LOGIN = "root";
+            Passwd pwd = posix.getpwnam(LOGIN);
+            assertNotNull(pwd);
+            assertEquals("Login name not equal", LOGIN, pwd.getLoginName());
+
+            assertTrue(pwd.getClass().equals(passwdClass));
+        }
+    }
+    @Test public void nonExistantUserReturnsNull() {
+        if (jnr.ffi.Platform.getNativePlatform().isUnix()) {
+            final String LOGIN = "dkjhfjkdsfhjksdhfsdjkhfsdkjhfdskj";
+            assertNull("getpwnam for non-existant user should return null", posix.getpwnam(LOGIN));
+        }
+    }
+}
diff --git a/src/test/java/jnr/posix/ProcessTest.java b/src/test/java/jnr/posix/ProcessTest.java
new file mode 100644
index 0000000..35a588f
--- /dev/null
+++ b/src/test/java/jnr/posix/ProcessTest.java
@@ -0,0 +1,136 @@
+package jnr.posix;
+
+import jnr.posix.DefaultNativeRLimit;
+import jnr.posix.DummyPOSIXHandler;
+import jnr.posix.POSIX;
+import jnr.posix.POSIXFactory;
+import jnr.posix.RLimit;
+import jnr.posix.util.Platform;
+import jnr.constants.platform.RLIMIT;
+import jnr.constants.platform.Sysconf;
+import jnr.ffi.Pointer;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class ProcessTest {
+
+
+    private static POSIX posix;
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        posix = POSIXFactory.getPOSIX(new DummyPOSIXHandler(), true);
+    }
+
+    @Test
+    public void times() {
+        long hz = posix.sysconf(Sysconf._SC_CLK_TCK);
+        if (Platform.IS_MAC) {
+            assertEquals("incorrect HZ value", 100L, hz);
+        }
+    }
+
+    @Test
+    public void getcwd() {
+        String propCwd = System.getProperty("user.dir");
+        assertEquals(propCwd, posix.getcwd());
+    }
+
+    @Test
+    public void testGetRLimit() {
+        if (Platform.IS_LINUX) {
+            RLimit rlim = posix.getrlimit(RLIMIT.RLIMIT_NPROC.intValue());
+
+            // These tests may fail depending on how the system they're being run on is configured.  Since they are
+            // system-defined limits that may even differ based on UID, it's hard to find something universally true.
+            // Limiting the number of processes a user can create is believed to be universally enabled since without it
+            // the system would be susceptible to fork bombs.
+            assertTrue("Bad soft limit for number of processes", rlim.rlimCur() > 0);
+            assertTrue("Bad hard limit for number of processes", rlim.rlimMax() > 0);
+        }
+    }
+
+    @Test
+    public void testGetRLimitPreallocatedRlimit() {
+        if (Platform.IS_LINUX) {
+            RLimit rlim = new DefaultNativeRLimit(jnr.ffi.Runtime.getSystemRuntime());
+
+            int result = posix.getrlimit(RLIMIT.RLIMIT_NPROC.intValue(), rlim);
+            assertEquals("getrlimit did not return 0", 0, result);
+
+            // These tests may fail depending on how the system they're being run on is configured.  Since they are
+            // system-defined limits that may even differ based on UID, it's hard to find something universally true.
+            // Limiting the number of processes a user can create is believed to be universally enabled since without it
+            // the system would be susceptible to fork bombs.
+            assertTrue("Bad soft limit for number of processes", rlim.rlimCur() > 0);
+            assertTrue("Bad hard limit for number of processes", rlim.rlimMax() > 0);
+        }
+    }
+
+    @Test
+    public void testGetRLimitPointer() {
+        if (Platform.IS_LINUX) {
+            Pointer rlim = jnr.ffi.Runtime.getSystemRuntime().getMemoryManager().allocateDirect(8 * 2); // 2 longs.
+
+            int result = posix.getrlimit(RLIMIT.RLIMIT_NPROC.intValue(), rlim);
+            assertEquals("getrlimit did not return 0", 0, result);
+
+            // These tests may fail depending on how the system they're being run on is configured.  Since they are
+            // system-defined limits that may even differ based on UID, it's hard to find something universally true.
+            // Limiting the number of processes a user can create is believed to be universally enabled since without it
+            // the system would be susceptible to fork bombs.
+            assertTrue("Bad soft limit for number of processes", rlim.getLong(0) > 0);
+            assertTrue("Bad hard limit for number of processes", rlim.getLong(8) > 0);
+        }
+    }
+
+    @Test
+    public void testSetRlimitLinux() {
+        if (Platform.IS_LINUX) {
+            RLimit originalRlim = posix.getrlimit(RLIMIT.RLIMIT_NPROC.intValue());
+
+            int result = posix.setrlimit(RLIMIT.RLIMIT_NPROC.intValue(), originalRlim.rlimCur() - 1, originalRlim.rlimMax() - 1);
+            assertEquals("setrlimit did not return 0", 0, result);
+
+            RLimit rlim = posix.getrlimit(RLIMIT.RLIMIT_NPROC.intValue());
+            assertEquals("soft limit didn't update", originalRlim.rlimCur() - 1, rlim.rlimCur());
+            assertEquals("hard limit didn't update", originalRlim.rlimMax() - 1, rlim.rlimMax());
+        }
+    }
+
+    @Test
+    public void testSetRlimitPreallocatedLinux() {
+        if (Platform.IS_LINUX) {
+            RLimit originalRlim = posix.getrlimit(RLIMIT.RLIMIT_NPROC.intValue());
+
+            RLimit updatedRlim = new DefaultNativeRLimit(jnr.ffi.Runtime.getSystemRuntime());
+            updatedRlim.init(originalRlim.rlimCur() - 1, originalRlim.rlimMax() - 1);
+
+            int result = posix.setrlimit(RLIMIT.RLIMIT_NPROC.intValue(), updatedRlim);
+            assertEquals("setrlimit did not return 0", 0, result);
+
+            RLimit rlim = posix.getrlimit(RLIMIT.RLIMIT_NPROC.intValue());
+            assertEquals("soft limit didn't update", originalRlim.rlimCur() - 1, rlim.rlimCur());
+            assertEquals("hard limit didn't update", originalRlim.rlimMax() - 1, rlim.rlimMax());
+        }
+    }
+
+    @Test
+    public void testSetRlimitPointerLinux() {
+        if (Platform.IS_LINUX) {
+            RLimit originalRlim = posix.getrlimit(RLIMIT.RLIMIT_NPROC.intValue());
+
+            Pointer updatedRlim = jnr.ffi.Runtime.getSystemRuntime().getMemoryManager().allocateDirect(8 * 2); // 2 longs.
+            updatedRlim.putLong(0, originalRlim.rlimCur() - 1);
+            updatedRlim.putLong(8, originalRlim.rlimMax() - 1);
+
+            int result = posix.setrlimit(RLIMIT.RLIMIT_NPROC.intValue(), updatedRlim);
+            assertEquals("setrlimit did not return 0", 0, result);
+
+            RLimit rlim = posix.getrlimit(RLIMIT.RLIMIT_NPROC.intValue());
+            assertEquals("soft limit didn't update", originalRlim.rlimCur() - 1, rlim.rlimCur());
+            assertEquals("hard limit didn't update", originalRlim.rlimMax() - 1, rlim.rlimMax());
+        }
+    }
+}
diff --git a/src/test/java/jnr/posix/SignalTest.java b/src/test/java/jnr/posix/SignalTest.java
new file mode 100644
index 0000000..01a8573
--- /dev/null
+++ b/src/test/java/jnr/posix/SignalTest.java
@@ -0,0 +1,70 @@
+package jnr.posix;
+
+import jnr.constants.platform.Signal;
+import jnr.posix.util.Platform;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class SignalTest {
+    private static POSIX posix;
+    private static POSIX javaPosix;
+    
+    @BeforeClass
+    public static void setupClass() throws Exception {
+        posix = POSIXFactory.getPOSIX(new DummyPOSIXHandler(), true);
+        javaPosix = new JavaPOSIX(new DummyPOSIXHandler());
+    }
+    
+    private static void waitUntilTrue(AtomicBoolean var, long maxWait) {
+        long start = System.currentTimeMillis();
+        while (!var.get() && (System.currentTimeMillis() - start) < maxWait) {
+            try {
+                Thread.sleep(1);
+            } catch (InterruptedException e) {
+                break;
+            }
+        }
+    }
+    
+    @Test
+    public void testBasicSignal() {
+        if (!Platform.IS_WINDOWS) {
+            Signal s = Signal.SIGHUP;
+            final AtomicBoolean fired = new AtomicBoolean(false);
+            posix.signal(s, new SignalHandler() {
+                public void handle(int signal) {
+                    fired.set(true);
+                }
+            });
+
+            posix.kill(posix.getpid(), s.intValue());
+            waitUntilTrue(fired, 200);
+            Assert.assertTrue(fired.get());
+        }
+    }
+    
+    @Test
+    public void testJavaSignal() {
+        if (!Platform.IS_WINDOWS) {
+            Signal s = Signal.SIGHUP;
+            final AtomicBoolean fired = new AtomicBoolean(false);
+            SignalHandler previousHandler = javaPosix.signal(s, new SignalHandler() {
+                public void handle(int signal) {
+                    fired.set(true);
+                }
+            });
+
+            Assert.assertNotNull(previousHandler);
+
+            // have to use native here; no abstraction for kill in pure Java
+            // TODO: sun.misc.Signal.raise can be used to kill current pid
+            posix.kill(posix.getpid(), s.intValue());
+
+            waitUntilTrue(fired, 200);
+            Assert.assertTrue(fired.get());
+        }
+    }
+}
diff --git a/src/test/java/jnr/posix/SpawnTest.java b/src/test/java/jnr/posix/SpawnTest.java
new file mode 100644
index 0000000..b98a098
--- /dev/null
+++ b/src/test/java/jnr/posix/SpawnTest.java
@@ -0,0 +1,200 @@
+package jnr.posix;
+
+import jnr.constants.platform.windows.OpenFlags;
+import jnr.ffi.Library;
+import jnr.ffi.Platform;
+import jnr.ffi.annotations.Out;
+import org.junit.*;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.Assert.*;
+import static jnr.posix.SpawnFileAction.*;
+
+public class SpawnTest {
+    private static POSIX posix;
+    private static LibC libc;
+    private static final List<String> emptyEnv = Arrays.asList(new String[0]);
+    private static final List<SpawnFileAction> emptyActions = Arrays.asList(new SpawnFileAction[0]);
+
+    public static interface LibC {
+        int pipe(@Out int[] fds);
+    }
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        if (Platform.getNativePlatform().isUnix()) {
+            posix = POSIXFactory.getPOSIX(new DummyPOSIXHandler(), true);
+            libc = Library.loadLibrary(LibC.class, jnr.ffi.Platform.getNativePlatform().getStandardCLibraryName());
+        }
+    }
+
+    @Test public void validPid() {
+        if (Platform.getNativePlatform().isUnix()) {
+            long pid = -1;
+            try {
+                pid = posix.posix_spawnp("true", emptyActions, Arrays.asList("true"), emptyEnv);
+                assertTrue(pid != -1);
+            } finally {
+                if (pid != -1) posix.libc().waitpid((int) pid, null, 0);
+            }
+        }
+    }
+
+    private static void closePipe(int[] fds) {
+        posix.libc().close(fds[0]);
+        posix.libc().close(fds[1]);
+    }
+
+    private static void killChild(long pid) {
+        if (pid > 0) {
+            posix.libc().kill((int) pid, 9); posix.libc().waitpid((int) pid, null, 0);
+        }
+    }
+
+    @Test public void outputPipe() {
+        if (Platform.getNativePlatform().isUnix()) {
+            int[] outputPipe = { -1, -1 };
+            long pid = -1;
+            try {
+                assertFalse(libc.pipe(outputPipe) < 0);
+                assertNotSame(-1, outputPipe[0]);
+                assertNotSame(-1, outputPipe[1]);
+
+                List<SpawnFileAction> actions = Arrays.asList(dup(outputPipe[1], 1));
+                pid = posix.posix_spawnp("echo", actions, Arrays.asList("echo", "bar"), emptyEnv);
+                assertTrue(pid != -1);
+
+                // close the write side of the output pipe, so read() will return immediately once the process has exited
+                posix.libc().close(outputPipe[1]);
+
+                ByteBuffer output = ByteBuffer.allocate(100);
+                long nbytes = posix.libc().read(outputPipe[0], output, output.remaining());
+                assertEquals(4L, nbytes);
+                output.position((int) nbytes).flip();
+                byte[] bytes = new byte[output.remaining()];
+                output.get(bytes);
+                assertEquals("bar", new String(bytes).trim());
+            } finally {
+                closePipe(outputPipe);
+                killChild(pid);
+            }
+        }
+    }
+
+    @Test public void inputPipe() {
+        if (Platform.getNativePlatform().isUnix()) {
+            int[] outputPipe = { -1, -1 };
+            int[] inputPipe = { -1, -1 };
+            long pid = -1;
+            try {
+            assertFalse(libc.pipe(outputPipe) < 0);
+                assertFalse(libc.pipe(inputPipe) < 0);
+                assertNotSame(-1, outputPipe[0]);
+                assertNotSame(-1, outputPipe[1]);
+                assertNotSame(-1, inputPipe[0]);
+                assertNotSame(-1, inputPipe[1]);
+
+                List<SpawnFileAction> actions = Arrays.asList(dup(inputPipe[0], 0), dup(outputPipe[1], 1));
+                pid = posix.posix_spawnp("cat", actions, Arrays.asList("cat", "-"), emptyEnv);
+                assertTrue(pid != -1);
+                posix.libc().close(inputPipe[0]);
+                assertEquals(3, posix.libc().write(inputPipe[1], ByteBuffer.wrap("foo".getBytes(Charset.forName("US-ASCII"))), 3));
+                posix.libc().close(inputPipe[1]); // send EOF to process
+
+                // close the write side of the output pipe, so read() will return immediately once the process has exited
+                posix.libc().close(outputPipe[1]);
+
+                ByteBuffer output = ByteBuffer.allocate(100);
+                long nbytes = posix.libc().read(outputPipe[0], output, output.remaining());
+                assertEquals(3L, nbytes);
+                output.position((int) nbytes).flip();
+                byte[] bytes = new byte[output.remaining()];
+                output.get(bytes);
+                assertEquals("foo", new String(bytes).trim());
+            } finally {
+                closePipe(outputPipe);
+                closePipe(inputPipe);
+                killChild(pid);
+            }
+        }
+    }
+
+    @Test public void inputFile() throws IOException {
+        if (Platform.getNativePlatform().isUnix()) {
+            File inputFile = File.createTempFile("foo", null);
+            FileOutputStream inputStream = new FileOutputStream(inputFile);
+            inputStream.write("foo".getBytes("US-ASCII"));
+            inputStream.close();
+            int[] outputPipe = { -1, -1 };
+            long pid = -1;
+            try {
+                assertFalse(libc.pipe(outputPipe) < 0);
+                assertNotSame(-1, outputPipe[0]);
+                assertNotSame(-1, outputPipe[1]);
+
+                List<SpawnFileAction> actions = Arrays.asList(open(inputFile.getAbsolutePath(), 0, OpenFlags.O_RDONLY.intValue(), 0444),
+                        dup(outputPipe[1], 1), close(outputPipe[0]));
+                pid = posix.posix_spawnp("cat", actions, Arrays.asList("cat", "-"), emptyEnv);
+                assertTrue(pid != -1);
+
+                // close the write side of the output pipe, so read() will return immediately once the process has exited
+                posix.libc().close(outputPipe[1]);
+
+                ByteBuffer output = ByteBuffer.allocate(100);
+                long nbytes = posix.libc().read(outputPipe[0], output, output.remaining());
+                assertEquals(3L, nbytes);
+                output.position((int) nbytes).flip();
+                byte[] bytes = new byte[output.remaining()];
+                output.get(bytes);
+                assertEquals("foo", new String(bytes).trim());
+            } finally {
+                closePipe(outputPipe);
+                killChild(pid);
+            }
+        }
+    }
+
+    @Test public void closeInput() throws IOException {
+        if (Platform.getNativePlatform().isUnix()) {
+            int[] outputPipe = { -1, -1 };
+            int[] inputPipe = { -1, -1 };
+            long pid = -1;
+            try {
+                assertFalse(libc.pipe(outputPipe) < 0);
+                assertFalse(libc.pipe(inputPipe) < 0);
+                assertNotSame(-1, outputPipe[0]);
+                assertNotSame(-1, outputPipe[1]);
+                assertNotSame(-1, inputPipe[0]);
+                assertNotSame(-1, inputPipe[1]);
+
+                List<SpawnFileAction> actions = Arrays.asList(dup(outputPipe[1], 1),
+                        open("/dev/null", 2, OpenFlags.O_WRONLY.intValue(), 0444),
+                        close(inputPipe[0]), close(inputPipe[1]));
+                pid = posix.posix_spawnp("cat", actions, Arrays.asList("cat", "/dev/fd/" + inputPipe[0]), emptyEnv);
+                assertTrue(pid != -1);
+                assertEquals(3, posix.libc().write(inputPipe[1], ByteBuffer.wrap("foo".getBytes(Charset.forName("US-ASCII"))), 3));
+                posix.libc().close(inputPipe[1]); // send EOF to process
+
+                // close the write side of the output pipe, so read() will return immediately once the process has exited
+                posix.libc().close(outputPipe[1]);
+
+                // Output from the process on stdout should be empty
+                ByteBuffer output = ByteBuffer.allocate(100);
+                long nbytes = posix.libc().read(outputPipe[0], output, output.remaining());
+                assertEquals(0L, nbytes);
+            } finally {
+                closePipe(inputPipe);
+                closePipe(outputPipe);
+                killChild(pid);
+            }
+        }
+    }
+}
diff --git a/src/test/java/jnr/posix/util/ConditionalTestRule.java b/src/test/java/jnr/posix/util/ConditionalTestRule.java
new file mode 100644
index 0000000..aa44a3f
--- /dev/null
+++ b/src/test/java/jnr/posix/util/ConditionalTestRule.java
@@ -0,0 +1,23 @@
+package jnr.posix.util;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+public abstract class ConditionalTestRule implements TestRule {
+    public Statement apply(Statement base, Description description) {
+        return statement(base);
+    }
+
+    private Statement statement(final Statement base) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                if (isSatisfied()) {
+                    base.evaluate();
+                }
+            }
+        };
+    }
+    public abstract  boolean isSatisfied();
+}
diff --git a/src/test/java/jnr/posix/util/DefaultPOSIXHandlerTest.java b/src/test/java/jnr/posix/util/DefaultPOSIXHandlerTest.java
new file mode 100644
index 0000000..f4ff76e
--- /dev/null
+++ b/src/test/java/jnr/posix/util/DefaultPOSIXHandlerTest.java
@@ -0,0 +1,25 @@
+package jnr.posix.util;
+
+import jnr.posix.POSIX;
+import jnr.posix.POSIXFactory;
+import org.junit.Test;
+
+import java.lang.management.ManagementFactory;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+public class DefaultPOSIXHandlerTest
+{
+    @Test
+    public void testGetPid() throws Exception
+    {
+        int pid_from_jmx = Integer.parseInt(ManagementFactory.getRuntimeMXBean().getName().split("@")[0]);
+
+        POSIX posix = POSIXFactory.getPOSIX(new DefaultPOSIXHandler(), true);
+        int pid = posix.getpid();
+        assertThat(pid, equalTo(pid_from_jmx));
+    }
+}
diff --git a/src/test/java/jnr/posix/util/PlatformTest.java b/src/test/java/jnr/posix/util/PlatformTest.java
new file mode 100644
index 0000000..7ae91ef
--- /dev/null
+++ b/src/test/java/jnr/posix/util/PlatformTest.java
@@ -0,0 +1,59 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: EPL 2.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Eclipse Public
+ * License Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+/**
+ * $Id: $
+ */
+package jnr.posix.util;
+
+import jnr.posix.util.Platform;
+import junit.framework.TestCase;
+
+/*
+ * Admittedly, there is a lot of cyclomatic complexity and system-dependent
+ * testing going on here.  At least it's in one place.
+ */
+
+public class PlatformTest extends TestCase {
+
+    public void testBitLengths()  {
+        assertTrue("Not 32 or 64-bit platform?", Platform.IS_32_BIT ^ Platform.IS_64_BIT);
+    }
+
+    public void testEnvCommand()  {
+        String command =Platform.envCommand();
+        if (Platform.IS_WINDOWS_9X)  {
+            assertEquals("Fails on Windows 95/98", "command.com /c set", command);
+        }
+        if (Platform.IS_WINDOWS & !Platform.IS_WINDOWS_9X)  {
+            assertEquals("Fails on Windows other than 95/98", "cmd.exe /c set", command);
+        }
+        if (!Platform.IS_WINDOWS)  {
+            assertEquals("Fails on Non-Windows platform", "env", command);
+        }
+    }
+}
diff --git a/src/test/java/jnr/posix/windows/WindowsFileTest.java b/src/test/java/jnr/posix/windows/WindowsFileTest.java
new file mode 100644
index 0000000..1f540ef
--- /dev/null
+++ b/src/test/java/jnr/posix/windows/WindowsFileTest.java
@@ -0,0 +1,317 @@
+package jnr.posix.windows;

+

+import java.io.File;

+import java.io.IOException;

+import java.io.RandomAccessFile;
+import java.nio.file.Files;
+import java.text.NumberFormat;

+import jnr.posix.DummyPOSIXHandler;

+import jnr.posix.FileStat;

+import jnr.posix.POSIX;

+import jnr.posix.POSIXFactory;

+import jnr.posix.WindowsPOSIX;

+import org.junit.BeforeClass;

+import org.junit.Test;

+

+import static org.junit.Assert.assertEquals;

+import static org.junit.Assert.assertFalse;

+import static org.junit.Assert.assertNotNull;

+import static org.junit.Assert.assertTrue;
+
+public class WindowsFileTest {

+    private static POSIX posix;

+

+    @BeforeClass

+    public static void setUpClass() throws Exception {

+        posix = POSIXFactory.getPOSIX(new DummyPOSIXHandler(), true);

+    }

+

+    private class Pair {

+        public File base;

+        public File leaf;

+        public Pair(File base, File leaf) {

+            this.base = base;

+            this.leaf = leaf;

+        }

+

+        public void cleanup() {

+            cleanup(base);

+        }

+

+        public void cleanup(File node) {

+            if (node.isDirectory()) {

+                File[] files = node.listFiles();

+                if (files != null) {

+                    for(File file: files) {

+                        cleanup(file);

+                    }

+                }

+            }

+            node.delete();

+        }

+    }

+    // FIXME: This is a broken method since it does not delete any of the generated dirs.

+    private static final String DIR_NAME = "0123456789";

+    private Pair makeLongPath() throws IOException {

+        File tmp = Files.createTempDirectory("temp" + Long.toHexString(System.nanoTime())).toFile();

+

+        StringBuilder buf = new StringBuilder(DIR_NAME);

+        for (int i = 0; i < 30; i++) {

+            buf.append(DIR_NAME).append('/');

+        }

+        File tmp2 = new File(tmp, buf.toString());

+        tmp2.mkdirs();

+

+        return new Pair(tmp, tmp2);

+    }

+

+    @Test

+    public void testLowercaseDriveLetter() throws Throwable {

+        // FIXME: Not all systems have a C drive but nearly all do

+        FileStat st = posix.stat("c:/");

+        assertTrue(st.dev() == 2);

+        assertTrue(st.rdev() == 2);

+    }

+

+    @Test

+    public void testFakeUIDGUID() throws Throwable {

+        File f = File.createTempFile("stat", null);

+

+        try {

+            FileStat st = posix.stat(f.getAbsolutePath());

+            assertTrue(st.uid() == 0);

+            assertTrue(st.gid() == 0);

+        } finally {

+            f.delete();

+        }

+    }

+

+    @Test

+    public void testExecutableSuffixesAreExecutable() throws Throwable {

+        File f = File.createTempFile("stat", ".exe");

+

+        try {

+            FileStat st = posix.stat(f.getAbsolutePath());

+            assertEquals("100755", Integer.toOctalString(st.mode()));

+            assertTrue(st.isExecutable());

+        } finally {

+            f.delete();

+        }

+

+        f = File.createTempFile("STAT", ".EXE");

+

+        try {

+            FileStat st = posix.stat(f.getAbsolutePath());

+            assertEquals("100755", Integer.toOctalString(st.mode()));

+            assertTrue(st.isExecutable());

+        } finally {

+            f.delete();

+        }

+

+    }

+

+    @Test

+    public void testBlocksAndBlockSizeReturn() throws Throwable {

+        File f = File.createTempFile("stat", null);

+

+        try {

+            FileStat st = posix.stat(f.getAbsolutePath());

+            assertTrue(st.blocks() == -1);

+            assertTrue(st.blockSize() == -1);

+        } finally {

+            f.delete();

+        }

+    }

+

+    @Test

+        public void testLongFileRegular() throws Throwable {

+        Pair pair = makeLongPath();

+        String path = pair.leaf.getAbsolutePath();

+        try {

+            FileStat st = posix.stat(path);

+            assertNotNull("posix.stat failed", st);

+

+            FileStat stat = posix.allocateStat();

+            int result = posix.stat(path, stat);

+            assertNotNull("posix.stat failed", stat);

+            assertEquals(0, result);

+        } finally {

+            pair.cleanup();

+        }

+    }

+

+    @Test

+    public void testLongFileUNC() throws Throwable {

+        Pair pair = makeLongPath();

+        String absolutePath = pair.leaf.getAbsolutePath();

+        char letter = absolutePath.charAt(0);

+        String path = absolutePath.replace(absolutePath.substring(0,2), "\\\\localhost\\" + letter + "$");

+        try {

+            FileStat st = posix.stat(path);

+            assertNotNull("posix.stat failed", st);

+

+            FileStat stat = posix.allocateStat();

+            int result = posix.stat(path, stat);

+            assertNotNull("posix.stat failed", stat);

+            assertEquals(0, result);

+        } finally {

+            pair.cleanup();

+        }

+    }

+

+    @Test

+    public void statUNCFile() throws Throwable {

+        File f = File.createTempFile("stat", null);

+        String absolutePath = f.getAbsolutePath();

+        char letter = absolutePath.charAt(0);

+        String path = absolutePath.replace(absolutePath.substring(0,2), "\\\\localhost\\" + letter + "$");

+        try {

+            FileStat st = posix.stat(path);

+            assertNotNull("posix.stat failed", st);

+

+            FileStat stat = posix.allocateStat();

+            int result = posix.stat(path, stat);

+            assertNotNull("posix.stat failed", stat);

+            assertEquals(0, result);

+        } finally {

+            f.delete();

+        }

+    }

+

+    @Test

+    public void unlinkTestWindows() throws Throwable {

+        File tmp = File.createTempFile("unlinkTest", "tmp");

+        RandomAccessFile raf = new RandomAccessFile(tmp, "rw");

+

+        raf.write("hello".getBytes());

+

+        // Windows won't allow you to delete open files, so we must

+        // close the handle before trying to delete it.  Unfortunately,

+        // this also means we're unable to write to the handle afterwards

+        // as we do with the non-Windows test.

+        raf.close();

+

+        int res = posix.unlink(tmp.getCanonicalPath());

+

+        assertEquals(0, res);

+        assertFalse(tmp.exists());

+    }

+

+    @Test

+    public void testFindFirstFile() throws Throwable {

+        File f = File.createTempFile("stat", null);

+        try {

+

+            POSIX posix = POSIXFactory.getNativePOSIX();

+            FileStat stat = posix.allocateStat();

+            int result = ((WindowsPOSIX) posix).findFirstFile(f.getAbsolutePath(), stat);

+

+            assertEquals(0, result);

+            assertTrue(stat.isFile());

+        } finally {

+            f.delete();

+        }

+    }

+

+    @Test

+    public void testFindFirstFileBogusFile() throws Throwable {

+        POSIX posix = POSIXFactory.getNativePOSIX();

+        FileStat stat = posix.allocateStat();

+        int result = ((WindowsPOSIX) posix).findFirstFile("sdjfhjfsdfhdsdfhsdj", stat);

+        assertTrue(result < 0);

+    }

+

+    @Test

+    public void utimensatWindows() throws Throwable {

+        File file = File.createTempFile("utimensat", null);

+        try {

+            FileStat fileStat = posix.stat(file.getPath());

+

+            long atimeSeconds = fileStat.atime() + 1;

+            long mtimeSeconds = fileStat.mtime() - 1;

+

+            // Windows precision is 100 ns

+            long atimeNanoSeconds = 123456700;

+            long mtimeNanoSeconds = 987654300;

+

+            // dirfd is ignored when passing an absolute path

+            // flag can be used to update symlinks

+            posix.utimensat(0,

+                    file.getAbsolutePath(),

+                    new long[]{atimeSeconds, atimeNanoSeconds},

+                    new long[]{mtimeSeconds, mtimeNanoSeconds},

+                    0);

+

+            fileStat = posix.stat(file.getPath());

+            assertEquals("access time should be updated", atimeSeconds, fileStat.atime());

+            assertEquals("modification time should be updated", mtimeSeconds, fileStat.mtime());

+        } finally {

+            file.delete();

+        }

+    }

+    

+    @Test

+    public void utimensatWindowsCurrentTime() throws Throwable {

+        File file = File.createTempFile("file", null);

+        try {

+            FileStat fileStat = posix.stat(file.getPath());

+            long atimeSeconds = fileStat.atime();

+            long mtimeSeconds = fileStat.mtime();

+            long atimeSecondsInPast = atimeSeconds - 1000;

+            long mtimeSecondsInPast = mtimeSeconds - 1000;

+

+            posix.utimensat(0,

+                    file.getAbsolutePath(),

+                    new long[]{atimeSecondsInPast, 0},

+                    new long[]{mtimeSecondsInPast, 0}, 0);

+            fileStat = posix.stat(file.getPath());

+            assertEquals("access time should be updated", fileStat.atime(), atimeSecondsInPast);

+            assertEquals("modification time should be updated", fileStat.mtime(), mtimeSecondsInPast);

+

+            posix.utimensat(0, file.getAbsolutePath(), null, null, 0);

+

+            fileStat = posix.stat(file.getPath());

+            assertTrue("access time should be updated to current time",

+                    timeWithinRange(fileStat.atime(), atimeSeconds, 10));

+            assertTrue("modification time should be updated to current time",

+                    timeWithinRange(fileStat.mtime(), mtimeSeconds, 10));

+        } finally {

+            file.delete();

+        }

+    }

+

+    @Test

+    public void utimenWindowsCurrentTime() throws Throwable {

+        File file = File.createTempFile("file", null);

+        try {

+            FileStat fileStat = posix.stat(file.getPath());

+            long atimeSeconds = fileStat.atime();

+            long mtimeSeconds = fileStat.mtime();

+            long atimeSecondsInPast = atimeSeconds - 1000;

+            long mtimeSecondsInPast = mtimeSeconds - 1000;

+

+            posix.utimes(file.getAbsolutePath(),

+                    new long[]{atimeSecondsInPast, 0},

+                    new long[]{mtimeSecondsInPast, 0});

+            fileStat = posix.stat(file.getPath());

+            assertEquals("access time should be updated",

+                    fileStat.atime(), atimeSecondsInPast);

+            assertEquals("modification time should be updated",

+                    fileStat.mtime(), mtimeSecondsInPast);

+

+            posix.utimes(file.getAbsolutePath(), null, null);

+

+            fileStat = posix.stat(file.getPath());

+            assertTrue("access time should be updated to current time",

+                    timeWithinRange(fileStat.atime(), atimeSeconds, 10));

+            assertTrue("modification time should be updated to current time",

+                    timeWithinRange(fileStat.mtime(), mtimeSeconds, 10));

+        } finally {

+            file.delete();

+        }

+    }

+

+    private boolean timeWithinRange(long actual, long expected, long precision) {

+        return actual > (expected - precision) && actual < (expected + precision);

+    }

+}

diff --git a/src/test/java/jnr/posix/windows/WindowsHelpersTest.java b/src/test/java/jnr/posix/windows/WindowsHelpersTest.java
new file mode 100644
index 0000000..09e6d69
--- /dev/null
+++ b/src/test/java/jnr/posix/windows/WindowsHelpersTest.java
@@ -0,0 +1,52 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: EPL 2.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Eclipse Public
+ * License Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+/**
+ * $Id: $
+ */
+package jnr.posix.windows;
+
+import jnr.posix.util.WindowsHelpers;
+import junit.framework.TestCase;
+
+public class WindowsHelpersTest extends TestCase {
+
+    public void testIsBatch() {
+        assertTrue(WindowsHelpers.isBatch("file.bat"));
+        assertTrue(WindowsHelpers.isBatch("FILE.BAT"));
+        assertTrue(WindowsHelpers.isBatch("file.cmd"));
+        assertTrue(WindowsHelpers.isBatch("FILE.CMD"));
+
+        assertFalse(WindowsHelpers.isBatch("file.exe"));
+        assertFalse(WindowsHelpers.isBatch("file.txt"));
+        assertFalse(WindowsHelpers.isBatch("file"));
+        assertFalse(WindowsHelpers.isBatch("filebat"));
+        assertFalse(WindowsHelpers.isBatch("filecmd"));
+        assertFalse(WindowsHelpers.isBatch(""));
+        assertFalse(WindowsHelpers.isBatch(null));
+    }
+}