diff --git a/rsync/COPYING b/rsync/COPYING
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/rsync/COPYING
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. 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
+them 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 prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. 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.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey 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;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU 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 that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ 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.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+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.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/rsync/Doxyfile b/rsync/Doxyfile
new file mode 100644
index 0000000..14e8d61
--- /dev/null
+++ b/rsync/Doxyfile
@@ -0,0 +1,187 @@
+# Doxyfile 1.2.15
+
+#---------------------------------------------------------------------------
+# General configuration options
+#---------------------------------------------------------------------------
+PROJECT_NAME = rsync
+PROJECT_NUMBER = HEAD
+OUTPUT_DIRECTORY = dox
+OUTPUT_LANGUAGE = English
+EXTRACT_ALL = YES
+EXTRACT_PRIVATE = YES
+EXTRACT_STATIC = YES
+EXTRACT_LOCAL_CLASSES = YES
+HIDE_UNDOC_MEMBERS = NO
+HIDE_UNDOC_CLASSES = NO
+BRIEF_MEMBER_DESC = YES
+REPEAT_BRIEF = YES
+ALWAYS_DETAILED_SEC = NO
+INLINE_INHERITED_MEMB = NO
+FULL_PATH_NAMES = NO
+STRIP_FROM_PATH = *source
+INTERNAL_DOCS = YES
+STRIP_CODE_COMMENTS = NO
+CASE_SENSE_NAMES = YES
+SHORT_NAMES = NO
+HIDE_SCOPE_NAMES = YES
+VERBATIM_HEADERS = YES
+SHOW_INCLUDE_FILES = YES
+JAVADOC_AUTOBRIEF = YES
+INHERIT_DOCS = YES
+INLINE_INFO = YES
+SORT_MEMBER_DOCS = NO
+DISTRIBUTE_GROUP_DOC = NO
+TAB_SIZE = 8
+GENERATE_TODOLIST = YES
+GENERATE_TESTLIST = YES
+GENERATE_BUGLIST = YES
+ALIASES =
+ENABLED_SECTIONS =
+MAX_INITIALIZER_LINES = 30
+OPTIMIZE_OUTPUT_FOR_C = YES
+OPTIMIZE_OUTPUT_JAVA = NO
+SHOW_USED_FILES = YES
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET = NO
+WARNINGS = NO
+WARN_IF_UNDOCUMENTED = NO
+WARN_FORMAT = "$file:$line: $text"
+WARN_LOGFILE =
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT = .
+FILE_PATTERNS = *.c \
+ *.h
+RECURSIVE = YES
+EXCLUDE = proto.h \
+ zlib \
+ popt
+EXCLUDE_SYMLINKS = NO
+EXCLUDE_PATTERNS =
+EXAMPLE_PATH =
+EXAMPLE_PATTERNS =
+EXAMPLE_RECURSIVE = NO
+IMAGE_PATH =
+INPUT_FILTER =
+FILTER_SOURCE_FILES = NO
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER = YES
+INLINE_SOURCES = YES
+REFERENCED_BY_RELATION = YES
+REFERENCES_RELATION = YES
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX = YES
+COLS_IN_ALPHA_INDEX = 3
+IGNORE_PREFIX =
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML = YES
+HTML_OUTPUT = html
+HTML_FILE_EXTENSION = .html
+HTML_HEADER =
+HTML_FOOTER =
+HTML_STYLESHEET =
+HTML_ALIGN_MEMBERS = YES
+GENERATE_HTMLHELP = NO
+GENERATE_CHI = NO
+BINARY_TOC = NO
+TOC_EXPAND = NO
+DISABLE_INDEX = NO
+ENUM_VALUES_PER_LINE = 3
+GENERATE_TREEVIEW = NO
+TREEVIEW_WIDTH = 250
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX = NO
+LATEX_OUTPUT = latex
+LATEX_CMD_NAME = latex
+MAKEINDEX_CMD_NAME = makeindex
+COMPACT_LATEX = NO
+PAPER_TYPE = a4wide
+EXTRA_PACKAGES =
+LATEX_HEADER =
+PDF_HYPERLINKS = YES
+USE_PDFLATEX = YES
+LATEX_BATCHMODE = YES
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF = NO
+RTF_OUTPUT = rtf
+COMPACT_RTF = NO
+RTF_HYPERLINKS = NO
+RTF_STYLESHEET_FILE =
+RTF_EXTENSIONS_FILE =
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN = NO
+MAN_OUTPUT = man
+MAN_EXTENSION = .3
+MAN_LINKS = NO
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML = NO
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF = NO
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING = NO
+MACRO_EXPANSION = NO
+EXPAND_ONLY_PREDEF = NO
+SEARCH_INCLUDES = YES
+INCLUDE_PATH =
+INCLUDE_FILE_PATTERNS =
+PREDEFINED =
+EXPAND_AS_DEFINED =
+SKIP_FUNCTION_MACROS = YES
+#---------------------------------------------------------------------------
+# Configuration::addtions related to external references
+#---------------------------------------------------------------------------
+TAGFILES =
+GENERATE_TAGFILE =
+ALLEXTERNALS = NO
+EXTERNAL_GROUPS = YES
+PERL_PATH = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS = YES
+HAVE_DOT = YES
+CLASS_GRAPH = YES
+COLLABORATION_GRAPH = YES
+TEMPLATE_RELATIONS = YES
+HIDE_UNDOC_RELATIONS = YES
+INCLUDE_GRAPH = YES
+INCLUDED_BY_GRAPH = YES
+GRAPHICAL_HIERARCHY = YES
+DOT_IMAGE_FORMAT = png
+DOT_PATH =
+DOTFILE_DIRS =
+MAX_DOT_GRAPH_WIDTH = 1024
+MAX_DOT_GRAPH_HEIGHT = 1024
+GENERATE_LEGEND = YES
+DOT_CLEANUP = YES
+#---------------------------------------------------------------------------
+# Configuration::addtions related to the search engine
+#---------------------------------------------------------------------------
+SEARCHENGINE = NO
+CGI_NAME = search.cgi
+CGI_URL =
+DOC_URL =
+DOC_ABSPATH =
+BIN_ABSPATH = /usr/local/bin/
+EXT_DOC_PATHS =
diff --git a/rsync/INSTALL b/rsync/INSTALL
new file mode 100644
index 0000000..6c016ad
--- /dev/null
+++ b/rsync/INSTALL
@@ -0,0 +1,74 @@
+To build and install rsync:
+
+ $ ./configure
+ $ make
+ # make install
+
+You may set the installation directory and other parameters by options
+to ./configure. To see them, use:
+
+ $ ./configure --help
+
+Configure tries to figure out if the local system uses group "nobody" or
+"nogroup" by looking in the /etc/group file. (This is only used for the
+default group of an rsync daemon, which attempts to run with "nobody"
+user and group permissions.) You can change the default user and group
+for the daemon by editing the NOBODY_USER and NOBODY_GROUP defines in
+config.h, or just override them in your /etc/rsyncd.conf file.
+
+As of 2.4.7, rsync uses Eric Troan's popt option-parsing library. A
+cut-down copy of a recent release is included in the rsync distribution,
+and will be used if there is no popt library on your build host, or if
+the --with-included-popt option is passed to ./configure.
+
+If you configure using --enable-maintainer-mode, then rsync will try
+to pop up an xterm on DISPLAY=:0 if it crashes. You might find this
+useful, but it should be turned off for production builds.
+
+MAKE COMPATIBILITY
+------------------
+
+Note that Makefile.in has a rule that uses a wildcard in a prerequisite. If
+your make has a problem with this rule, you will see an error like this:
+
+ Don't know how to make ./*.c
+
+You can change the "proto.h-tstamp" target in Makefile.in to list all the *.c
+filenames explicitly in order to avoid this issue.
+
+RPM NOTES
+---------
+
+Under packaging you will find .spec files for several distributions.
+The .spec file in packaging/lsb can be used for Linux systems that
+adhere to the Linux Standards Base (e.g., RedHat and others).
+
+HP-UX NOTES
+-----------
+
+The HP-UX 10.10 "bundled" C compiler seems not to be able to cope with
+ANSI C. You may see this error message in config.log if ./configure
+fails:
+
+ (Bundled) cc: "configure", line 2162: error 1705: Function prototypes are an ANSI feature.
+
+Install gcc or HP's "ANSI/C Compiler".
+
+MAC OSX NOTES
+-------------
+
+Some versions of Mac OS X (Darwin) seem to have an IPv6 stack, but do
+not completely implement the "New Sockets" API.
+
+ says that Apple started to support
+IPv6 in 10.2 (Jaguar). If your build fails, try again after running
+configure with --disable-ipv6.
+
+IBM AIX NOTES
+-------------
+
+IBM AIX has a largefile problem with mkstemp. See IBM PR-51921.
+The workaround is to append the below to config.h
+ #ifdef _LARGE_FILES
+ #undef HAVE_SECURE_MKSTEMP
+ #endif
diff --git a/rsync/Makefile.in b/rsync/Makefile.in
new file mode 100644
index 0000000..2cb50bc
--- /dev/null
+++ b/rsync/Makefile.in
@@ -0,0 +1,323 @@
+# Makefile for rsync. This is processed by configure to produce the final
+# Makefile
+
+prefix=@prefix@
+datarootdir=@datarootdir@
+exec_prefix=@exec_prefix@
+stunnel4=@STUNNEL4@
+bindir=@bindir@
+mandir=@mandir@
+
+LIBS=@LIBS@
+CC=@CC@
+CFLAGS=@CFLAGS@
+CPPFLAGS=@CPPFLAGS@
+EXEEXT=@EXEEXT@
+LDFLAGS=@LDFLAGS@
+
+INSTALLCMD=@INSTALL@
+INSTALLMAN=@INSTALL@
+
+srcdir=@srcdir@
+MKDIR_P=@MKDIR_P@
+VPATH=$(srcdir)
+SHELL=/bin/sh
+
+VERSION=@RSYNC_VERSION@
+
+.SUFFIXES:
+.SUFFIXES: .c .o
+
+GENFILES=configure.sh config.h.in proto.h proto.h-tstamp rsync.1 rsyncd.conf.5
+HEADERS=byteorder.h config.h errcode.h proto.h rsync.h ifuncs.h itypes.h inums.h \
+ lib/pool_alloc.h
+LIBOBJ=lib/wildmatch.o lib/compat.o lib/snprintf.o lib/mdfour.o lib/md5.o \
+ lib/permstring.o lib/pool_alloc.o lib/sysacls.o lib/sysxattrs.o @LIBOBJS@
+zlib_OBJS=zlib/deflate.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o \
+ zlib/trees.o zlib/zutil.o zlib/adler32.o zlib/compress.o zlib/crc32.o
+OBJS1=flist.o rsync.o generator.o receiver.o cleanup.o sender.o exclude.o \
+ util.o util2.o main.o checksum.o match.o syscall.o log.o backup.o delete.o
+OBJS2=options.o io.o compat.o hlink.o token.o uidlist.o socket.o hashtable.o \
+ fileio.o batch.o clientname.o chmod.o acls.o xattrs.o
+OBJS3=progress.o pipe.o
+DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o
+popt_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \
+ popt/popthelp.o popt/poptparse.o
+OBJS=$(OBJS1) $(OBJS2) $(OBJS3) $(DAEMON_OBJ) $(LIBOBJ) @BUILD_ZLIB@ @BUILD_POPT@
+
+TLS_OBJ = tls.o syscall.o lib/compat.o lib/snprintf.o lib/permstring.o lib/sysxattrs.o @BUILD_POPT@
+
+# Programs we must have to run the test cases
+CHECK_PROGS = rsync$(EXEEXT) tls$(EXEEXT) getgroups$(EXEEXT) getfsdev$(EXEEXT) \
+ testrun$(EXEEXT) trimslash$(EXEEXT) t_unsafe$(EXEEXT) wildtest$(EXEEXT)
+
+CHECK_SYMLINKS = testsuite/chown-fake.test testsuite/devices-fake.test testsuite/xattrs-hlink.test
+
+# Objects for CHECK_PROGS to clean
+CHECK_OBJS=tls.o testrun.o getgroups.o getfsdev.o t_stub.o t_unsafe.o trimslash.o wildtest.o
+
+# note that the -I. is needed to handle config.h when using VPATH
+.c.o:
+@OBJ_SAVE@
+ $(CC) -I. -I$(srcdir) $(CFLAGS) $(CPPFLAGS) -c $< @CC_SHOBJ_FLAG@
+@OBJ_RESTORE@
+
+all: Makefile rsync$(EXEEXT) rsync-ssl stunnel-rsync stunnel-rsyncd.conf @MAKE_MAN@
+
+install: all
+ -${MKDIR_P} ${DESTDIR}${bindir}
+ ${INSTALLCMD} ${INSTALL_STRIP} -m 755 rsync$(EXEEXT) ${DESTDIR}${bindir}
+ -${MKDIR_P} ${DESTDIR}${mandir}/man1
+ -${MKDIR_P} ${DESTDIR}${mandir}/man5
+ if test -f rsync.1; then ${INSTALLMAN} -m 644 rsync.1 ${DESTDIR}${mandir}/man1; fi
+ if test -f rsyncd.conf.5; then ${INSTALLMAN} -m 644 rsyncd.conf.5 ${DESTDIR}${mandir}/man5; fi
+
+install-ssl-client: rsync-ssl stunnel-rsync
+ -${MKDIR_P} ${DESTDIR}${bindir}
+ ${INSTALLCMD} ${INSTALL_STRIP} -m 755 rsync-ssl ${DESTDIR}${bindir}
+ ${INSTALLCMD} ${INSTALL_STRIP} -m 755 stunnel-rsync ${DESTDIR}${bindir}
+
+install-ssl-daemon: stunnel-rsyncd.conf
+ -${MKDIR_P} ${DESTDIR}/etc/stunnel
+ ${INSTALLCMD} ${INSTALL_STRIP} -m 644 stunnel-rsyncd.conf ${DESTDIR}/etc/stunnel/rsyncd.conf
+ @if ! ls /etc/rsync-ssl/certs/server.* >/dev/null 2>/dev/null; then \
+ echo "Note that you'll need to install the certificate used by /etc/stunnel/rsyncd.conf"; \
+ fi
+
+install-all: install install-ssl-client install-ssl-daemon
+
+install-strip:
+ $(MAKE) INSTALL_STRIP='-s' install
+
+rsync$(EXEEXT): $(OBJS)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
+
+$(OBJS): $(HEADERS)
+$(CHECK_OBJS): $(HEADERS)
+
+flist.o: rounding.h
+
+rounding.h: rounding.c rsync.h
+ @for r in 0 1 3; do \
+ if $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o rounding -DEXTRA_ROUNDING=$$r -I. $(srcdir)/rounding.c >rounding.out 2>&1; then \
+ echo "#define EXTRA_ROUNDING $$r" >rounding.h; \
+ if test -f "$$HOME/build_farm/build_test.fns"; then \
+ echo "EXTRA_ROUNDING is $$r" >&2; \
+ fi; \
+ break; \
+ fi; \
+ done
+ @rm -f rounding
+ @if test -f rounding.h; then : ; else \
+ cat rounding.out 1>&2; \
+ echo "Failed to create rounding.h!" 1>&2; \
+ exit 1; \
+ fi
+ @rm -f rounding.out
+
+tls$(EXEEXT): $(TLS_OBJ)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(TLS_OBJ) $(LIBS)
+
+testrun$(EXEEXT): testrun.o
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ testrun.o
+
+getgroups$(EXEEXT): getgroups.o
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ getgroups.o $(LIBS)
+
+getfsdev$(EXEEXT): getfsdev.o
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ getfsdev.o $(LIBS)
+
+TRIMSLASH_OBJ = trimslash.o syscall.o lib/compat.o lib/snprintf.o
+trimslash$(EXEEXT): $(TRIMSLASH_OBJ)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(TRIMSLASH_OBJ) $(LIBS)
+
+T_UNSAFE_OBJ = t_unsafe.o syscall.o util.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/wildmatch.o
+t_unsafe$(EXEEXT): $(T_UNSAFE_OBJ)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(T_UNSAFE_OBJ) $(LIBS)
+
+gen: conf proto.h man
+
+gensend: gen
+ rsync -aivzc $(GENFILES) samba.org:/home/ftp/pub/rsync/generated-files/
+
+conf:
+ cd $(srcdir) && $(MAKE) -f prepare-source.mak conf
+
+configure.sh config.h.in: configure.ac aclocal.m4
+ @if test -f configure.sh; then cp -p configure.sh configure.sh.old; else touch configure.sh.old; fi
+ @if test -f config.h.in; then cp -p config.h.in config.h.in.old; else touch config.h.in.old; fi
+ autoconf -o configure.sh
+ autoheader && touch config.h.in
+ @if diff configure.sh configure.sh.old >/dev/null 2>&1; then \
+ echo "configure.sh is unchanged."; \
+ rm configure.sh.old; \
+ else \
+ echo "configure.sh has CHANGED."; \
+ fi
+ @if diff config.h.in config.h.in.old >/dev/null 2>&1; then \
+ echo "config.h.in is unchanged."; \
+ rm config.h.in.old; \
+ else \
+ echo "config.h.in has CHANGED."; \
+ fi
+ @if test -f configure.sh.old -o -f config.h.in.old; then \
+ if test "$(MAKECMDGOALS)" = reconfigure; then \
+ echo 'Continuing with "make reconfigure".'; \
+ else \
+ echo 'You may need to run:'; \
+ echo ' make reconfigure'; \
+ exit 1; \
+ fi \
+ fi
+
+reconfigure: configure.sh
+ ./config.status --recheck
+ ./config.status
+
+Makefile: Makefile.in config.status configure.sh config.h.in
+ @if test -f Makefile; then cp -p Makefile Makefile.old; else touch Makefile.old; fi
+ @./config.status
+ @if diff Makefile Makefile.old >/dev/null 2>&1; then \
+ echo "Makefile is unchanged."; \
+ rm Makefile.old; \
+ else \
+ if test "$(MAKECMDGOALS)" = reconfigure; then \
+ echo 'Continuing with "make reconfigure".'; \
+ else \
+ echo "Makefile updated -- rerun your make command."; \
+ exit 1; \
+ fi \
+ fi
+
+rsync-ssl: $(srcdir)/rsync-ssl.in Makefile
+ sed 's;\@bindir\@;$(bindir);g' <$(srcdir)/rsync-ssl.in >rsync-ssl
+ @chmod +x rsync-ssl
+
+stunnel-rsync: $(srcdir)/stunnel-rsync.in Makefile
+ sed 's;\@stunnel4\@;$(stunnel4);g' <$(srcdir)/stunnel-rsync.in >stunnel-rsync
+ @chmod +x stunnel-rsync
+
+stunnel-rsyncd.conf: $(srcdir)/stunnel-rsyncd.conf.in Makefile
+ sed 's;\@bindir\@;$(bindir);g' <$(srcdir)/stunnel-rsyncd.conf.in >stunnel-rsyncd.conf
+
+proto: proto.h-tstamp
+
+proto.h: proto.h-tstamp
+ @if test -f proto.h; then :; else cp -p $(srcdir)/proto.h .; fi
+
+proto.h-tstamp: $(srcdir)/*.c $(srcdir)/lib/compat.c config.h
+ perl $(srcdir)/mkproto.pl $(srcdir)/*.c $(srcdir)/lib/compat.c
+
+man: rsync.1 rsyncd.conf.5 man-copy
+
+man-copy:
+ @-if test -f rsync.1; then :; else echo 'Copying srcdir rsync.1'; cp -p $(srcdir)/rsync.1 .; fi
+ @-if test -f rsyncd.conf.5; then :; else echo 'Copying srcdir rsyncd.conf.5'; cp -p $(srcdir)/rsyncd.conf.5 .; fi
+
+rsync.1: rsync.yo
+ yodl2man -o rsync.1 $(srcdir)/rsync.yo
+ -$(srcdir)/tweak_manpage rsync.1
+
+rsyncd.conf.5: rsyncd.conf.yo
+ yodl2man -o rsyncd.conf.5 $(srcdir)/rsyncd.conf.yo
+ -$(srcdir)/tweak_manpage rsyncd.conf.5
+
+clean: cleantests
+ rm -f *~ $(OBJS) $(CHECK_PROGS) $(CHECK_OBJS) $(CHECK_SYMLINKS) \
+ rounding rounding.h *.old
+
+cleantests:
+ rm -rf ./testtmp*
+
+# We try to delete built files from both the source and build
+# directories, just in case somebody previously configured things in
+# the source directory.
+distclean: clean
+ rm -f Makefile config.h config.status
+ rm -f rsync-ssl stunnel-rsync stunnel-rsyncd.conf
+ rm -f lib/dummy popt/dummy zlib/dummy
+ rm -f $(srcdir)/Makefile $(srcdir)/config.h $(srcdir)/config.status
+ rm -f $(srcdir)/lib/dummy $(srcdir)/popt/dummy $(srcdir)/zlib/dummy
+ rm -f config.cache config.log
+ rm -f $(srcdir)/config.cache $(srcdir)/config.log
+ rm -f shconfig $(srcdir)/shconfig
+ rm -f $(GENFILES)
+ rm -rf autom4te.cache
+
+# this target is really just for my use. It only works on a limited
+# range of machines and is used to produce a list of potentially
+# dead (ie. unused) functions in the code. (tridge)
+finddead:
+ nm *.o */*.o |grep 'U ' | awk '{print $$2}' | sort -u > nmused.txt
+ nm *.o */*.o |grep 'T ' | awk '{print $$3}' | sort -u > nmfns.txt
+ comm -13 nmused.txt nmfns.txt
+
+# 'check' is the GNU name, 'test' is the name for everybody else :-)
+.PHONY: check test
+
+test: check
+
+# There seems to be no standard way to specify some variables as
+# exported from a Makefile apart from listing them like this.
+
+# This depends on building rsync; if we need any helper programs it
+# should depend on them too.
+
+# We try to run the scripts with POSIX mode on, in the hope that will
+# catch Bash-isms earlier even if we're running on GNU. Of course, we
+# might lose in the future where POSIX diverges from old sh.
+
+check: all $(CHECK_PROGS) $(CHECK_SYMLINKS)
+ rsync_bin=`pwd`/rsync$(EXEEXT) $(srcdir)/runtests.sh
+
+check29: all $(CHECK_PROGS) $(CHECK_SYMLINKS)
+ rsync_bin=`pwd`/rsync$(EXEEXT) $(srcdir)/runtests.sh --protocol=29
+
+check30: all $(CHECK_PROGS) $(CHECK_SYMLINKS)
+ rsync_bin=`pwd`/rsync$(EXEEXT) $(srcdir)/runtests.sh --protocol=30
+
+wildtest.o: wildtest.c lib/wildmatch.c rsync.h config.h
+wildtest$(EXEEXT): wildtest.o lib/compat.o lib/snprintf.o @BUILD_POPT@
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ wildtest.o lib/compat.o lib/snprintf.o @BUILD_POPT@ $(LIBS)
+
+testsuite/chown-fake.test:
+ ln -s chown.test $(srcdir)/testsuite/chown-fake.test
+
+testsuite/devices-fake.test:
+ ln -s devices.test $(srcdir)/testsuite/devices-fake.test
+
+testsuite/xattrs-hlink.test:
+ ln -s xattrs.test $(srcdir)/testsuite/xattrs-hlink.test
+
+# This does *not* depend on building or installing: you can use it to
+# check a version installed from a binary or some other source tree,
+# if you want.
+
+installcheck: $(CHECK_PROGS) $(CHECK_SYMLINKS)
+ POSIXLY_CORRECT=1 TOOLDIR=`pwd` rsync_bin="$(bindir)/rsync$(EXEEXT)" srcdir="$(srcdir)" $(srcdir)/runtests.sh
+
+# TODO: Add 'dist' target; need to know which files will be included
+
+# Run the SPLINT (Secure Programming Lint) tool.
+.PHONY: splint
+splint:
+ splint +unixlib +gnuextensions -weak rsync.c
+
+
+rsync.dvi: doc/rsync.texinfo
+ texi2dvi -o $@ $<
+
+rsync.ps: rsync.dvi
+ dvips -ta4 -o $@ $<
+
+rsync.pdf: doc/rsync.texinfo
+ texi2dvi -o $@ --pdf $<
+
+
+doxygen:
+ cd $(srcdir) && rm dox/html/* && doxygen
+
+# for maintainers only
+doxygen-upload:
+ rsync -avzv $(srcdir)/dox/html/ --delete \
+ samba.org:/home/httpd/html/rsync/doxygen/head/
diff --git a/rsync/NEWS b/rsync/NEWS
new file mode 100644
index 0000000..1cbbe9b
--- /dev/null
+++ b/rsync/NEWS
@@ -0,0 +1,115 @@
+NEWS for rsync 3.1.1 (22 Jun 2014)
+Protocol: 31 (unchanged)
+Changes since 3.1.0:
+
+ BUG FIXES:
+
+ - If the receiver gets bogus filenames from the sender (an unexpected
+ leading slash or a ".." infix dir), exit with an error. This prevents a
+ malicious sender from trying to inject filenames that would affect an
+ area outside the destination directories.
+
+ - Fixed a failure to remove the partial-transfer temp file when interrupted
+ (and rsync is not saving the partial files).
+
+ - Changed the chown/group/xattr-set order to avoid losing some security-
+ related xattr info (that would get cleared by a chown).
+
+ - Fixed a bug in the xattr-finding code that could make a non-root-run
+ receiver not able to find some xattr numbers.
+
+ - Fixed a bug in the early daemon protocol where a timeout failed to be
+ honored (e.g. if the remote side fails to send us the initial protocol
+ greeting).
+
+ - Fixed unintended inclusion of commas in file numbers in the daemon log.
+
+ - We once again send the 'f' sub-flag (of -e) to the server side so it
+ knows that we can handle incremental-recursion directory errors properly
+ in older protocols.
+
+ - Fixed an issue with too-aggressive keep-alive messages causing a problem
+ for older rsync versions early in the transfer.
+
+ - Fixed an incorrect message about backup-directory-creation when using
+ --dry-run and the backup dir is not an absolute path.
+
+ - Fixed a bug where a failed deletion and/or a failed sender-side removal
+ would not affect the exit code.
+
+ - Fixed a bug that caused a failure when combining --delete-missing-args
+ with --xattrs and/or --acls.
+
+ - Fixed a strange dir_depth assertion error that was caused by empty-dir
+ removals and/or duplicate files in the transfer.
+
+ - Fixed a problem with --info=progress2's output stats where rsync would
+ only update the stats at the end of each file's transfer. It now uses
+ the data that is flowing for the current file, making the stats more
+ accurate and less jumpy.
+
+ - Fixed an itemize bug that affected the combo of --link-dest, -X, and -n.
+
+ - Fixed a problem with delete messages not appearing in the log file when
+ the user didn't use --verbose.
+
+ - Improve chunked xattr reading for OS X.
+
+ - Removed an attempted hard-link xattr optimization that was causing a
+ transfer failure. This removal is flagged in the compatibility code, so
+ if a better fix can be discovered, we have a way to flip it on again.
+
+ - Fixed a bug when the receiver is not configured to be able to hard link
+ symlimks/devices/special-file items but the sender sent some of these
+ items flagged as hard-linked.
+
+ - We now generate a better error if the buffer overflows in do_mknod().
+
+ - Fixed a problem reading more than 16 ACLs on some OSes.
+
+ - Fixed the reading of the secrets file to avoid an infinite wait when
+ the username is missing.
+
+ - Fixed a parsing problem in the --usermap/--groupmap options when using
+ MIN-MAX numbers.
+
+ - Switched Cygwin back to using socketpair "pipes" to try to speed it up.
+
+ - Added knowledge of a few new options to rrsync.
+
+ ENHANCEMENTS:
+
+ - Tweaked the temp-file naming when --temp-dir=DIR is used: the temp-file
+ names will not get a '.' prepended.
+
+ - Added support for a new-compression idiom that does not compress all the
+ matching data in a transfer. This can help rsync to use less cpu when a
+ transfer has a lot of matching data, and also makes rsync compatible with
+ a non-bundled zlib. See the --new-compress and --old-compress options in
+ the manpage.
+
+ - Added the support/rsync-no-vanished wrapper script.
+
+ - Made configure more prominently mention when we failed to find yodl (in
+ case the user wants to be able to generate manpages from *.yo files).
+
+ - Have manpage mention how a daemon's max-verbosity setting affects info
+ and debug options. Also added more clarification on backslash removals
+ for excludes that contain wildcards.
+
+ - Have configure check if for the attr lib (for getxattr) for those systems
+ that need to link against it explicitly.
+
+ - Change the early dir-creation logic to only use that idiom in an
+ inc-recursive copy that is preserving directory times. e.g. using
+ --omit-dir-times will avoid these early directories being created.
+
+ - Fix a bug in cmp_time() that would return a wrong result if the 2 times
+ differed by an amount greater than what a time_t can hold.
+
+ DEVELOPER RELATED:
+
+ - We now include an example systemd file (in packaging/systemd).
+
+ - Tweaked configure to make sure that any intended use of the included popt
+ and/or zlib code is put early in the CFLAGS.
diff --git a/rsync/OLDNEWS b/rsync/OLDNEWS
new file mode 100644
index 0000000..ba02b07
--- /dev/null
+++ b/rsync/OLDNEWS
@@ -0,0 +1,3599 @@
+NEWS for rsync 3.1.0 (28 Sep 2013)
+Protocol: 31 (changed)
+Changes since 3.0.9:
+
+ OUTPUT CHANGES:
+
+ - Output numbers in 3-digit groups by default (e.g. 1,234,567). See the
+ --human-readable option for a way to turn it off. See also the daemon's
+ "log format" parameter and related command-line options (including
+ --out-format) for a modifier that can be used to request digit-grouping
+ or human-readable output in log escapes. (Note that log output is
+ unchanged by default.)
+
+ - The --list-only option is now affected by the --human-readable setting.
+ It will display digit groupings by default, and unit suffixes if higher
+ levels of readability are requested. Also, the column width for the size
+ output has increased from 11 to 14 characters when human readability is
+ enabled. Use --no-h to get the old-style output and column size.
+
+ - The output of the --progress option has changed: the string "xfer" was
+ shortened to "xfr", and the string "to-check" was shortened to "to-chk",
+ both designed to make room for the (by default) wider display of file
+ size numbers without making the total line-length longer. Also, when
+ incremental recursion is enabled, the string "ir-chk" will be used
+ instead of "to-chk" up until the incremental-recursion scan is done,
+ letting you know that the value to check and the total value will still
+ be increasing as new files are found.
+
+ - Enhanced the --stats output: 1) to mention how many files were created
+ (protocol >= 28), 2) to mention how many files were deleted (a new line
+ for protocol 31, but only output when --delete is in effect), and 3) to
+ follow the file-count, created-count, and deleted-count with a subcount
+ list that shows the counts by type. The wording of the transferred count
+ has also changed so that it is clearer that it is only a count of regular
+ files.
+
+ BUG FIXES:
+
+ - Fixed a bug in the iconv code when EINVAL or EILSEQ is returned with a
+ full output buffer.
+
+ - Fixed some rare bugs in --iconv processing that might cause a multibyte
+ character to get translated incorrectly.
+
+ - Fixed a bogus "vanished file" error if some files were specified with
+ "./" prefixes and others were not.
+
+ - Fixed a bug in --sparse where an extra gap could get inserted after a
+ partial write.
+
+ - Changed the way --progress overwrites its prior output in order to make
+ it nearly impossible for the progress to get overwritten by an error.
+
+ - Improved the propagation of abnormal-exit error messages. This should
+ help the client side to receive errors from the server when it is exiting
+ abnormally, and should also avoid dying with an "connection unexpectedly
+ closed" exit when the closed connection is really expected.
+
+ - The sender now checks each file it plans to remove to ensure that it
+ hasn't changed from the first stat's info. This helps to avoid losing
+ file data when the user is not using the option in a safe manner.
+
+ - Fixed a data-duplication bug in the compress option that made compression
+ less efficient. This improves protocol 31 onward, while behaving in a
+ compatible (buggy) manner with older rsync protocols.
+
+ - When creating a temp-file, rsync is now a bit smarter about it dot-char
+ choices, which can fix a problem on OS X with names that start with "..".
+
+ - Rsync now sets a cleanup flag for --inplace and --append transfers that
+ will flush the write buffer if the transfer aborts. This ensures that
+ more received data gets written out to the disk on an aborted transfer
+ (which is quite helpful on a slow, flaky connection).
+
+ - The reads that map_ptr() now does are aligned on 1K boundaries. This
+ helps some filesystems and/or files that don't like unaligned reads.
+
+ - Fix an issue in the msleep() function if time jumps backwards.
+
+ - Fix daemon-server module-name splitting bug where an arg would get split
+ even if --protect-args was used.
+
+ ENHANCEMENTS:
+
+ - Added the --remote-option=OPT (-M OPT) command-line option that is useful
+ for things like sending a remote --log-file=FILE or --fake-super option.
+
+ - Added the --info=FLAGS and --debug=FLAGS options to allow finer-grained
+ control over what is output. Added an extra type of --progress output
+ using --info=progress2.
+
+ - The --msgs2stderr option can help with debugging rsync by allowing the
+ debug messages to get output to stderr rather than travel via the socket
+ protocol.
+
+ - Added the --delete-missing-args and --ignore-missing-args options to
+ either delete or ignore user-specified files on the receiver that are
+ missing on the sender (normally the absence of user-specified files
+ generates an error).
+
+ - Added a "T" (terabyte) category to the --human-readable size suffixes.
+
+ - Added the --usermap/--groupmap/--chown options for manipulating file
+ ownership during the copy.
+
+ - Added the "%C" escape to the log-output handling, which will output the
+ MD5 checksum of any transferred file, or all files if --checksum was
+ specified (when protocol 30 or above is in effect).
+
+ - Added the "reverse lookup" parameter to the rsync daemon config file to
+ allow reverse-DNS lookups to be disabled.
+
+ - Added a forward-DNS lookup for the daemon's hosts allow/deny config. Can
+ be disabled via "forward lookup" parameter (defaults to enabled).
+
+ - Added a way for more than one group to be specified in the daemon's
+ config file, including a way to specify that you want all of the
+ specified user's groups without having to name them. Also changed the
+ daemon to complain about an inability to set explicitly-specified uid/gid
+ values, even when not run by a super-user.
+
+ - The daemon now tries to send the user the error messages from the
+ pre-xfer exec script when it fails.
+
+ - Improved the use of alt-dest options into an existing hierarchy of files:
+ If a match is found in an alt-dir, it takes precedence over an existing
+ file. (We'll need to wait for a future version before attribute-changes
+ on otherwise unchanged files are safe when using an existing hierarchy.)
+
+ - Added per-user authorization options and group-authorization support to
+ the daemon's "auth users" parameter.
+
+ - Added a way to reference environment variables in a daemon's config file
+ (using %VAR% references).
+
+ - When replacing a non-dir with a symlink/hard-link/device/special-file,
+ the update should now be done in an atomic manner.
+
+ - Avoid re-sending xattr info for hard-linked files w/the same xattrs
+ (protocol 31).
+
+ - The backup code was improved to use better logic maintaining the backup
+ directory hierarchy. Also, when a file is being backed up, rsync tries
+ to hard-link it into place so that the upcoming replacement of the
+ destination file will be atomic (for the normal, non-inplace logic).
+
+ - Added the ability to synchronize nano-second modified times.
+
+ - Added a few more default suffixes for the "dont compress" settings.
+
+ - Added the checking of the RSYNC_PROTECT_ARGS environment variable to allow
+ the default for the --protect-args command-line option to be overridden.
+
+ - Added the --preallocate command-line option.
+
+ - Allow --password-file=- to read the password from stdin (filename "-").
+
+ - Rsync now comes packaged with an rsync-ssl helper script that can be
+ used to contact a remote rsync daemon using a piped-stunnel command.
+ It also includes an stunnel config file to run the server side to
+ support ssl daemon connections. See the packaging/lsb/rsync.spec
+ file for one way to package the resulting files. (Suggestions for
+ how to make this even easier to install & use are welcomed.)
+
+ - Improved the speed of some --inplace updates when there are lots of
+ identical checksum blocks that end up being unusable.
+
+ - Added the --outbuf=N|L|B option for choosing the output buffering.
+
+ - Repeating the --fuzzy option now causes the code to look for fuzzy
+ matches inside alt-dest directories too.
+
+ - The --chmod option now supports numeric modes, e.g. --chmod=644,D755
+
+ - Added some Solaris xattr code.
+
+ - Made an rsync daemon (the listening process) exit with a 0 status when
+ it was signaled to die. This helps launchd.
+
+ - Improved the RSYNC_* environment variables for the pre-xfer exec script:
+ when a daemon is sent multiple request args, they are now joined into a
+ single return value (separated by spaces) so that the RSYNC_REQUEST
+ environment variable is accurate for any "pre-xfer exec". The values in
+ RSYNC_ARG# vars are no longer truncated at the "." arg (prior to the
+ request dirs/files), so that all the requested values are also listed
+ (separately) in RSYNC_ARG# variables.
+
+ EXTRAS:
+
+ - Added an "instant-rsyncd" script to the support directory, which makes
+ it easy to configure a simple rsync daemon in the current directory.
+
+ - Added the "mapfrom" and "mapto" scripts to the support directory, which
+ makes it easier to do user/group mapping in a local transfer based on
+ passwd/group files from another machine.
+
+ - There's a new, improved version of the lsh script in the support dir:
+ it's written in perl and supports -u without resorting to using sudo
+ (when run as root). The old shell version is now named lsh.sh.
+
+ - There is a helper script named rsync-slash-strip in the support directory
+ for anyone that wants to change the way rsync handles args with trailing
+ slashes. (e.g. arg/ would get stripped to arg while arg/. would turn into
+ arg/).
+
+ INTERNAL:
+
+ - The I/O code was rewritten to be simpler and do bigger buffered reads
+ over the socket. The I/O between the receiver and the generator was
+ changed to be standard multiplexed-I/O (like that over the socket).
+
+ - The sender tries to use any dead time while the generator is looking for
+ files to transfer in order to do sender-side directory scanning in a more
+ parallel manner.
+
+ - A daemon can now inform a client about a daemon-configured timeout value
+ so that the client can assist in the keep-alive activity (protocol 31).
+
+ - The filter code received some refactoring to make it more extendible, to
+ read better, and do better sanity checking.
+
+ - Really big numbers are now output using our own big-num routine rather
+ than casting them to a double and using a %.0f conversion.
+
+ - The pool_alloc library has received some minor improvements in alignment
+ handling.
+
+ - Added init_stat_x() function to avoid duplication of acl/xattr init code.
+
+ - The included zlib was upgraded from 1.2.3 to 1.2.8.
+
+ - Rsync can now be compiled to use an unmodified zlib library instead of
+ the tweaked one that is included with rsync. This will eventually
+ become the default, at which point we'll start the countdown to removing
+ the included zlib. Until then, feel free to configure using:
+
+ ./configure --with-included-zlib=no
+
+ DEVELOPER RELATED:
+
+ - Added more conditional debug output.
+
+ - Fixed some build issues for android and minix.
+
+NEWS for rsync 3.0.9 (23 Sep 2011)
+Protocol: 30 (unchanged)
+Changes since 3.0.8:
+
+ BUG FIXES:
+
+ - Fix a crash bug in checksum scanning when --inplace is used.
+
+ - Fix a hang if a hard-linked file cannot be opened by the sender (e.g.
+ if it has no read permission).
+
+ - Fix preservation of a symlink's system xattrs (e.g. selinux) on Linux.
+
+ - Fix a memory leak in the xattr code.
+
+ - Fixed a bug with --delete-excluded when a filter merge file has a rule
+ that specifies a receiver-only side restriction.
+
+ - Fix a bug with the modifying of unwritable directories.
+
+ - Fix --fake-super's interaction with --link-dest same-file comparisons.
+
+ - Fix the updating of the curr_dir buffer to avoid a duplicate slash.
+
+ - Fix the directory permissions on an implied dot-dir when using --relative
+ (e.g. /outside/path/././send/path).
+
+ - Fixed some too-long sleeping instances when using --bwlimit.
+
+ - Fixed when symlink ownership difference-checking gets compiled into
+ unchanged_attrs().
+
+ - Improved the socket-error reporting when multiple protocols fail.
+
+ - Fixed a case where a socket error could reference just-freed memory.
+
+ - Failing to use a password file that was specified on the command-line is
+ now a fatal error.
+
+ - Fix the non-root updating of directories that don't have the read and/or
+ execute permission.
+
+ - Make daemon-excluded file errors more error-like.
+
+ - Fix a compilation issue on older C compilers (due to a misplaced var
+ declaration).
+
+ - Make configure avoid finding socketpair on cygwin.
+
+ - Avoid trying to reference SO_BROADCAST if the OS doesn't support it.
+
+ - Fix some issues with the post-processing of the man pages.
+
+ - Fixed the user home-dir handling in the support/lsh script.
+
+ - Some minor manpage improvements.
+
+NEWS for rsync 3.0.8 (26 Mar 2011)
+Protocol: 30 (unchanged)
+Changes since 3.0.7:
+
+ BUG FIXES:
+
+ - Fixed two buffer-overflow issues: one where a directory path that is
+ exactly MAXPATHLEN was not handled correctly, and one handling a
+ --backup-dir that is extra extra large.
+
+ - Fixed a data-corruption issue when preserving hard-links without
+ preserving file ownership, and doing deletions either before or during
+ the transfer (CVE-2011-1097). This fixes some assert errors in the
+ hard-linking code, and some potential failed checksums (via -c) that
+ should have matched.
+
+ - Fixed a potential crash when an rsync daemon has a filter/exclude list
+ and the transfer is using ACLs or xattrs.
+
+ - Fixed a hang if a really large file is being processed by an rsync that
+ can't handle 64-bit numbers. Rsync will now complain about the file
+ being too big and skip it.
+
+ - For devices and special files, we now avoid gathering useless ACL and/or
+ xattr information for files that aren't being copied. (The un-copied
+ files are still put into the file list, but there's no need to gather
+ data that is not going to be used.) This ensures that if the user uses
+ --no-D, that rsync can't possibly complain about being unable to gather
+ extended information from special files that are in the file list (but
+ not in the transfer).
+
+ - Properly handle requesting remote filenames that start with a dash. This
+ avoids a potential error where a filename could be interpreted as a
+ (usually invalid) option.
+
+ - Fixed a bug in the comparing of upper-case letters in file suffixes for
+ --skip-compress.
+
+ - If an rsync daemon has a module configured without a path setting, rsync
+ will now disallow access to that module.
+
+ - If the destination arg is an empty string, it will be treated as a
+ reference to the current directory (as 2.x used to do).
+
+ - If rsync was compiled with a newer time-setting function (such as
+ lutimes), rsync will fall-back to an older function (such as utimes) on a
+ system where the newer function is not around. This helps to make the
+ rsync binary more portable in mixed-OS-release situations.
+
+ - Fixed a batch-file writing bug that would not write out the full set of
+ compatibility flags that the transfer was using. This fixes a potential
+ protocol problem for a batch file that contains a sender-side I/O error:
+ it would have been sent in a way that the batch-reader wasn't expecting.
+
+ - Some improvements to the hard-linking code to ensure that device-number
+ hashing is working right, and to supply more information if the hard-link
+ code fails.
+
+ - The --inplace code was improved to not search for an impossible checksum
+ position. The quadruple-verbose chunk[N] message will now mention when
+ an inplace chunk was handled by a seek rather than a read+write.
+
+ - Improved ACL mask handling, e.g. for Solaris.
+
+ - Fixed a bug that prevented --numeric-ids from disabling the translation
+ of user/group IDs for ACLs.
+
+ - Fixed an issue where an xattr and/or ACL transfer that used an alt-dest
+ option (e.g. --link-dest) could output an error trying to itemize the
+ changes against the alt-dest directory's xattr/ACL info but was instead
+ trying to access the not-yet-existing new destination directory.
+
+ - Improved xattr system-error messages to mention the full path to the
+ file.
+
+ - The --link-dest checking for identical symlinks now avoids considering
+ attribute differences that cannot be changed on the receiver.
+
+ - Avoid trying to read/write xattrs on certain file types for certain OSes.
+ Improved configure to set NO_SYMLINK_XATTRS, NO_DEVICE_XATTRS, and/or
+ NO_SPECIAL_XATTRS defines in config.h.
+
+ - Improved the unsafe-symlink errors messages.
+
+ - Fixed a bug setting xattrs on new files that aren't user writable.
+
+ - Avoid re-setting xattrs on a hard-linked file w/the same xattrs.
+
+ - Fixed a bug with --fake-super when copying files and dirs that aren't
+ user writable.
+
+ - Fixed a bug where a sparse file could have its last sparse block turned
+ into a real block when rsync sets the file size (requires ftruncate).
+
+ - If a temp-file name is too long, rsync now avoids truncating the name in
+ the middle of adjacent high-bit characters. This prevents a potential
+ filename error if the filesystem doesn't allow a name to contain an
+ invalid multi-byte sequence.
+
+ - If a muli-protocol socket connection fails (i.e., when contacting a
+ daemon), we now report all the failures, not just the last one. This
+ avoids losing a relevant error (e.g. an IPv4 connection-refused error)
+ that happened before the final error (e.g. an IPv6 protocol-not-supported
+ error).
+
+ - Generate a transfer error if we try to call chown with a -1 for a uid or
+ a gid (which is not settable).
+
+ - Fixed the working of --force when used with --one-file-system.
+
+ - Fix the popt arg parsing so that an option that doesn't take an arg will
+ reject an attempt to supply one (can configure --with-included-popt if
+ your system's popt library doesn't yet have this fix).
+
+ - A couple minor option tweaks to the support/rrsync script, and also some
+ regex changes that make vim highlighting happier.
+
+ - Fixed some issues in the support/mnt-excl script.
+
+ - Various manpage improvements.
+
+ ENHANCEMENTS:
+
+ - Added ".hg/" to the default cvs excludes (see -C & --cvs-exclude).
+
+ DEVELOPER RELATED:
+
+ - Use lchmod() whenever it is available (not just on symlinks).
+
+ - A couple fixes to the socketpair_tcp() routine.
+
+ - Updated the helper scripts in the packaging subdirectory.
+
+ - Renamed configure.in to configure.ac.
+
+ - Fixed configure's checking for iconv routines for newer OS X versions.
+
+ - Fixed the testsuite/xattrs.test script on OS X.
+
+NEWS for rsync 3.0.7 (31 Dec 2009)
+Protocol: 30 (unchanged)
+Changes since 3.0.6:
+
+ BUG FIXES:
+
+ - Fixed a bogus free when using --xattrs with --backup.
+
+ - Avoid an error when --dry-run was trying to stat a prior hard-link file
+ that hasn't really been created.
+
+ - Fixed a problem with --compress (-z) where the receiving side could
+ return the error "inflate (token) returned -5".
+
+ - Fixed a bug where --delete-during could delete in a directory before it
+ noticed that the sending side sent an I/O error for that directory (both
+ sides of the transfer must be at least 3.0.7).
+
+ - Improved --skip-compress's error handling of bad character-sets and got
+ rid of a lingering debug fprintf().
+
+ - Fixed the daemon's conveyance of io_error value from the sender.
+
+ - An rsync daemon use seteuid() (when available) if it used setuid().
+
+ - Get the permissions right on a --fake-super transferred directory that
+ needs more owner permissions to emulate root behavior.
+
+ - An absolute-path filter rule (i.e. with a '/' modifier) no longer loses
+ its modifier when sending the filter rules to the remote rsync.
+
+ - Improved the "--delete does not work without -r or -d" message.
+
+ - Improved rsync's handling of --timeout to avoid a weird timeout case
+ where the sender could timeout even though it has recently written data
+ to the socket (but hasn't read data recently, due to the writing).
+
+ - Some misc manpage improvements.
+
+ - Fixed the chmod-temp-dir testsuite on a system without /var/tmp.
+
+ - Make sure that a timeout specified in the daemon's config is used as a
+ maximum timeout value when the user also specifies a timeout.
+
+ - Improved the error-exit reporting when rsync gets an error trying to
+ cleanup after an error: the initial error is reported.
+
+ - Improved configure's detection of IPv6 for solaris and cygwin.
+
+ - The AIX sysacls routines will now return ENOSYS if ENOTSUP is missing.
+
+ - Made our (only used if missing) getaddrinfo() routine use inet_pton()
+ (which we also provide) instead of inet_aton().
+
+ - The exit-related debug messages now mention the program's role so it is
+ clear who output what message.
+
+ DEVELOPER RELATED:
+
+ - Got rid of type-punned compiler warnings output by newer gcc versions.
+
+ - The Makefile now ensures that proto.h will be rebuilt if config.h changes.
+
+ - The testsuite no longer uses "id -u", so it works better on solaris.
+
+
+NEWS for rsync 3.0.6 (8 May 2009)
+Protocol: 30 (unchanged)
+Changes since 3.0.5:
+
+ BUG FIXES:
+
+ - Fixed a --read-batch hang when rsync is reading a batch file that was
+ created from an incremental-recursion transfer.
+
+ - Fixed the daemon's socket code to handle the simultaneous arrival of
+ multiple connections.
+
+ - Fix --safe-links/--copy-unsafe-links to properly handle symlinks that
+ have consecutive slashes in the value.
+
+ - Fixed the parsing of an [IPv6_LITERAL_ADDR] when a USER@ is prefixed.
+
+ - The sender now skips a (bogus) symlink that has a 0-length value, which
+ avoids a transfer error in the receiver.
+
+ - Fixed a case where the sender could die with a tag-0 error if there was
+ an I/O during the sending of the file list.
+
+ - Fixed the rrsync script to avoid a server-side problem when -e is at the
+ start of the short options.
+
+ - Fixed a problem where a vanished directory could turn into an exit code
+ 23 instead of the proper exit code 24.
+
+ - Fixed the --iconv conversion of symlinks when doing a local copy.
+
+ - Fixed a problem where --one-file-system was not stopping deletions on the
+ receiving side when a mount-point directory did not match a directory in
+ the transfer.
+
+ - Fixed the dropping of an ACL mask when no named ACL values were present.
+
+ - Fixed an ACL/xattr corruption issue where the --backup option could cause
+ rsync to associate the wrong ACL/xattr information with received files.
+
+ - Fixed the use of --xattrs with --only-write-batch.
+
+ - Fixed the use of --dry-run with --read-batch.
+
+ - Fixed configure's erroneous use of target.
+
+ - Fixed configure's --disable-debug option.
+
+ - Fixed a run-time issue for systems that can't find iconv_open() by adding
+ the --disable-iconv-open configure option.
+
+ - Complain and die if the user tries to combine --remove-source-files (or
+ the deprecated --remove-sent-files) with --read-batch.
+
+ - Fixed an failure transferring special files from Solaris to Linux.
+
+
+NEWS for rsync 3.0.5 (28 Dec 2008)
+Protocol: 30 (unchanged)
+Changes since 3.0.4:
+
+ BUG FIXES:
+
+ - Initialize xattr data in a couple spots in the hlink code, which avoids a
+ crash when the xattr pointer's memory happens to start out non-zero.
+ Also fixed the itemizing of an alt-dest file's xattrs when hard-linking.
+
+ - Don't send a bogus "-" option to an older server if there were no short
+ options specified.
+
+ - Fixed skipping of unneeded updates in a batch file when incremental
+ recursion is active. Added a test for this. Made batch-mode handle
+ "redo" files properly (and without hanging).
+
+ - Fix the %P logfile escape when the daemon logs from inside a chroot.
+
+ - Fixed the use of -s (--protect-args) when used with a remote source or
+ destination that had an empty path (e.g. "host:"). Also fixed a problem
+ when -s was used when accessing a daemon via a remote-shell.
+
+ - Fixed the use of a dot-dir path (e.g. foo/./bar) inside a --files-from
+ file when the root of the transfer isn't the current directory.
+
+ - Fixed a bug with "-K --delete" removing symlinks to directories when
+ incremental recursion is active.
+
+ - Fixed a hard to trigger hang when using --remove-source-files.
+
+ - Got rid of an annoying delay when accessing a daemon via a remote-shell.
+
+ - Properly ignore (superfluous) source args on a --read-batch command.
+
+ - Improved the manpage's description of the '*' wildcard to remove the
+ confusing "non-empty" qualifier.
+
+ - Fixed reverse lookups in the compatibility-library version of
+ getnameinfo().
+
+ - Fixed a bug when using --sparse on a sparse file that has over 2GB of
+ consecutive sparse data.
+
+ - Avoid a hang when using at least 3 --verbose options on a transfer with a
+ client sender (which includes local copying).
+
+ - Fixed a problem with --delete-delay reporting an error when it was ready
+ to remove a directory that was now gone.
+
+ - Got rid of a bunch of "warn_unused_result" compiler warnings.
+
+ - If an ftruncate() on a received file fails, it now causes a partial-
+ transfer warning.
+
+ - Allow a path with a leading "//" to be preserved (CYGWIN only).
+
+ ENHANCEMENTS:
+
+ - Made the support/atomic-rsync script able to perform a fully atomic
+ update of the copied hierarchy when the destination is setup using a
+ particular symlink idiom.
+
+
+NEWS for rsync 3.0.4 (6 Sep 2008)
+Protocol: 30 (unchanged)
+Changes since 3.0.3:
+
+ BUG FIXES:
+
+ - Fixed a bug in the hard-linking code where it would sometimes try to
+ allocate 0 bytes of memory (which fails on some OSes, such as AIX).
+
+ - Fixed the hard-linking of files from a device that has a device number
+ of 0 (which seems to be a common device number on NetBSD).
+
+ - Fixed the handling of a --partial-dir that cannot be created. This
+ particularly impacts the --delay-updates option (since the files cannot
+ be delayed without a partial-dir), and was potentially destructive if
+ the --remove-source-files was also specified.
+
+ - Fixed a couple issues in the --fake-super handling of xattrs when the
+ destination files have root-level attributes (e.g. selinux values) that
+ a non-root copy can't affect.
+
+ - Improved the keep-alive check in the generator to fire consistently in
+ incremental-recursion mode when --timeout is enabled.
+
+ - The --iconv option now converts the content of a symlink too, instead
+ of leaving it in the wrong character-set (requires 3.0.4 on both sides
+ of the transfer).
+
+ - When using --iconv, if a filename fails to convert on the receiving side,
+ this no longer makes deletions in the root-dir of the transfer fail
+ silently (the user now gets a warning about deletions being disabled
+ due to IO error as long as --ignore-errors was not specified).
+
+ - When using --iconv, if a server-side receiver can't convert a filename,
+ the error message sent back to the client no longer mangles the name
+ with the wrong charset conversion.
+
+ - Fixed a potential alignment issue in the IRIX ACL code when allocating
+ the initial "struct acl" object. Also, cast mallocs to avoid warnings.
+
+ - Changed some errors that were going to stdout to go to stderr.
+
+ - Made human_num() and human_dnum() able to output a negative number
+ (rather than outputting a cryptic string of punctuation).
+
+ ENHANCEMENTS:
+
+ - Rsync will avoid sending an -e option to the server if an older protocol
+ is requested (and thus the option would not be useful). This lets the
+ user specify the --protocol=29 option to access an overly-restrictive
+ server that is rejecting the protocol-30 use of -e to the server.
+
+ - Improved the message output for an RERR_PARTIAL exit.
+
+ DEVELOPER RELATED:
+
+ - The Makefile will not halt for just a timestamp change on the Makefile
+ or the configure files, only for actual changes in content.
+
+ - Changed some commands in the testsuite's xattrs.test that called "rsync"
+ instead of "$RSYNC".
+
+ - Enhanced the release scripts to be able to handle a branch release and
+ to do even more consistency checks on the files.
+
+
+NEWS for rsync 3.0.3 (29 Jun 2008)
+Protocol: 30 (unchanged)
+Changes since 3.0.2:
+
+ BUG FIXES:
+
+ - Fixed a wildcard matching problem in the daemon when a module has
+ "use chroot" enabled.
+
+ - Fixed a crash bug in the hard-link code.
+
+ - Fixed the sending of xattr directory information when the code finds a
+ --link-dest or --copy-dest directory with unchanged xattrs -- the
+ destination directory now gets these unchanged xattrs properly applied.
+
+ - Fixed an xattr-sending glitch that could cause an "Internal abbrev"
+ error.
+
+ - Fixed the combination of --xattrs and --backup.
+
+ - The generator no longer allows a '.' dir to be excluded by a daemon-
+ exclude rule.
+
+ - Fixed deletion handling when copying a single, empty directory (with no
+ files) to a differently named, non-existent directory.
+
+ - Fixed the conversion of spaces into dashes in the %M log escape.
+
+ - Fixed several places in the code that were not returning the right
+ errno when a function failed.
+
+ - Fixed the backing up of a device or special file into a backup dir.
+
+ - Moved the setting of the socket options prior to the connect().
+
+ - If rsync exits in the middle of a --progress output, it now outputs a
+ newline to help prevent the progress line from being overwritten.
+
+ - Fixed a problem with how a destination path with a trailing slash or
+ a trailing dot-dir was compared against the daemon excludes.
+
+ - Fixed the sending of large (size > 16GB) files when talking to an older
+ rsync (protocols < 30): we now use a compatible block size limit.
+
+ - If a file's length is so huge that we overflow a checksum buffer count
+ (i.e. several hundred TB), warn the user and avoid sending an invalid
+ checksum struct over the wire.
+
+ - If a source arg is excluded, --relative no longer adds the excluded
+ arg's implied dirs to the transfer. This fix also made the exclude
+ check happen in the better place in the sending code.
+
+ - Use the overflow_exit() function for overflows, not out_of_memory().
+
+ - Improved the code to better handle a system that has only 32-bit file
+ offsets.
+
+ ENHANCEMENTS:
+
+ - The rsyncd.conf manpage now consistently refers to the parameters in
+ the daemon config file as "parameters".
+
+ - The description of the --inplace option was improved.
+
+ EXTRAS:
+
+ - Added a new script in the support directory, deny-rsync, which allows
+ an admin to (temporarily) replace the rsync command with a script that
+ sends an error message to the remote client via the rsync protocol.
+
+ DEVELOPER RELATED:
+
+ - Fixed a testcase failure if the tests are run as root and made some
+ compatibility improvements.
+
+ - Improved the daemon tests, including checking module comments, the
+ listing of files, and the ensuring that daemon excludes can't affect
+ a dot-dir arg.
+
+ - Improved some build rules for those that build in a separate directory
+ from the source, including better install rules for the man pages, and
+ the fixing of a proto.h-tstamp rule that could make the binaries get
+ rebuild without cause.
+
+ - Improved the testsuite to work around a problem with some utilities
+ (e.g. cp -p & touch -r) rounding sub-second timestamps.
+
+ - Ensure that the early patches don't cause any generated-file hunks to
+ bleed-over into patches that follow.
+
+
+NEWS for rsync 3.0.2 (8 Apr 2008)
+Protocol: 30 (unchanged)
+Changes since 3.0.1:
+
+ BUG FIXES:
+
+ - Fixed a potential buffer overflow in the xattr code.
+
+ ENHANCEMENTS:
+
+ - None.
+
+ DEVELOPER RELATED:
+
+ - The RPM spec file was improved to install more useful files.
+
+ - A few developer-oriented scripts were moved from the support dir
+ to the packaging dir.
+
+
+NEWS for rsync 3.0.1 (3 Apr 2008)
+Protocol: 30 (unchanged)
+Changes since 3.0.0:
+
+ NOTABLE CHANGES IN BEHAVIOR:
+
+ - Added the 'c'-flag to the itemizing of non-regular files so that the
+ itemized output doesn't get hidden if there were no attribute changes,
+ and also so that the itemizing of a --copy-links run will distinguish
+ between copying an identical non-regular file and the creation of a
+ revised version with a new value (e.g. a changed symlink referent, a
+ new device number, etc.).
+
+ BUG FIXES:
+
+ - Fixed a crash bug when a single-use rsync daemon (via remote shell) was
+ run without specifying a --config=FILE option.
+
+ - Fixed a crash when backing up a directory that has a default ACL.
+
+ - Fixed a bug in the handling of xattr values that could cause rsync to
+ not think that a file's extended attributes are up-to-date.
+
+ - Fixed the working of --fake-super with --link-dest and --xattrs.
+
+ - Fixed a hang when combining --dry-run with --remove-source-files.
+
+ - Fixed a bug with --iconv's handling of files that cannot be converted:
+ a failed name can no longer cause a transfer failure.
+
+ - Fixed the building of the rounding.h file on systems that need custom
+ CPPFLAGS to be used. Also improved the error reporting if the building
+ of rounding.h fails.
+
+ - Fixed the use of the --protect-args (-s) option when talking to a daemon.
+
+ - Fixed the --ignore-existing option's protection of files on the receiver
+ that are non-regular files on the sender (e.g. if a symlink or a dir on
+ the sender is trying to replace a file on the receiver). The reverse
+ protection (protecting a dir/symlink/device from being replaced by a
+ file) was already working.
+
+ - Fixed an assert failure if --hard-links is combined with an option that
+ can skip a file in a set of hard-linked files (i.e. --ignore-existing,
+ --append, etc.), without skipping all the files in the set.
+
+ - Avoid setting the modify time on a directory that already has the right
+ modify time set. This avoids tweaking the dir's ctime.
+
+ - Improved the daemon-exclude handling to do a better job of applying the
+ exclude rules to path entries. It also sends the user an error just as
+ if the files were actually missing (instead of silently ignoring the
+ user's args), and avoids sending the user the filter-action messages
+ for these non-user-initiated rules.
+
+ - Fixed some glitches with the dry-run code's missing-directory
+ handling, including a problem when combined with --fuzzy.
+
+ - Fixed some glitches with the skipped-directory handling.
+
+ - Fixed the 'T'-flag itemizing of symlinks when --time isn't preserved.
+
+ - Fixed a glitch in the itemizing of permissions with the -E option.
+
+ - The --append option's restricting of transfers to those that add data no
+ longer prevents the updating of non-content changes to otherwise up-to-
+ date files (i.e. those with the same content but differing permissions,
+ ownership, xattrs, etc.).
+
+ - Don't allow --fake-super to be specified with -XX (double --xattrs)
+ because the options conflict. If a daemon has "fake super" enabled,
+ it automatically downgrades a -XX request to -X.
+
+ - Fixed a couple bugs in the parsing of daemon-config excludes that could
+ make a floating exclude rule get treated as matching an absolute path.
+
+ - A daemon doesn't try to auto-refuse the "iconv" option if iconv-support
+ wasn't compiled in to the daemon (avoiding a warning in the logs).
+
+ - Fixed the inclusion of per-dir merge files from implied dirs.
+
+ - Fixed the support/rrsync script to work with the latest options that
+ rsync sends (including its flag-specifying use of -e to the server).
+
+ ENHANCEMENTS:
+
+ - Added the --old-dirs (--old-d) option to make it easier for a user to
+ ask for file-listings with older rsync versions (this is easier than
+ having to type "-r --exclude='/*/*'" manually).
+
+ - When getting an error while asking an older rsync daemon for a file
+ listing, rsync will try to notice if the error is a rejection of the
+ --dirs (-d) option and let the user know how to work around the issue.
+
+ - Added a few more --no-OPTION overrides.
+
+ - Improved the documentation of the --append option.
+
+ - Improved the documentation of the filter/exclude/include daemon
+ parameters.
+
+ INTERNAL:
+
+ - Fixed a couple minor bugs in the included popt library (ones which I
+ sent to the official popt project for inclusion in the 1.14 release).
+
+ - Fixed a stat() call that should have been do_stat() so that the proper
+ normal/64-bit stat() function gets called. (Was in an area that should
+ not have caused problems, though.)
+
+ - Changed the file-glob code to do a directory scan without using the
+ "glob" and "glob.h". This lets us do the globbing with less memory
+ churn, and also avoid adding daemon-excluded items to the returned
+ args.
+
+ DEVELOPER RELATED:
+
+ - The configure script tries to get the user's compiler to not warn about
+ unused function parameters if the build is not including one or more of
+ the ACL/xattrs/iconv features.
+
+ - The configure script now has better checks for figuring out if the
+ included popt code should be used or not.
+
+ - Fixed two testsuite glitches: avoid a failure if someone's "cd" command
+ outputs the current directory when cd-ing to a relative path, and made
+ the itemized test query how rsync was built to determine if it should
+ expect hard-linked symlinks or not.
+
+ - Updated the testsuite to verify that various bug fixes remain fixed.
+
+ - The RPM spec file was updated to have: (1) comments for how to use the
+ rsync-patch tar file, and (2) an /etc/xinetd.d/rsync file.
+
+ - Updated the build scripts to work with a revised FTP directory
+ structure.
+
+
+NEWS for rsync 3.0.0 (1 Mar 2008)
+Protocol: 30 (changed)
+Changes since 2.6.9:
+
+ NOTABLE CHANGES IN BEHAVIOR:
+
+ - The handling of implied directories when using --relative has changed to
+ send them as directories (e.g. no implied dir is ever sent as a symlink).
+ This avoids unexpected behavior and should not adversely affect most
+ people. If you're one of those rare individuals who relied upon having
+ an implied dir be duplicated as a symlink, you should specify the
+ transfer of the symlink and the transfer of the referent directory as
+ separate args. (See also --keep-dirlinks and --no-implied-dirs.)
+ Also, exclude rules no longer have a partial effect on implied dirs.
+
+ - Requesting a remote file-listing without specifying -r (--recursive) now
+ sends the -d (--dirs) option to the remote rsync rather than sending -r
+ along with an extra exclude of /*/*. If the remote rsync does not
+ understand the -d option (i.e. it is 2.6.3 or older), you will need to
+ either turn off -d (--no-d), or specify -r --exclude='/*/*' manually.
+
+ - In --dry-run mode, the last line of the verbose summary text is output
+ with a "(DRY RUN)" suffix to help remind you that no updates were made.
+ Similarly, --only-write-batch outputs "(BATCH ONLY)".
+
+ - A writable rsync daemon with "use chroot" disabled now defaults to a
+ symlink-munging behavior designed to make symlinks safer while also
+ allowing absolute symlinks to be stored and retrieved. This also has
+ the effect of making symlinks unusable while they're in the daemon's
+ hierarchy. See the daemon's "munge symlinks" parameter for details.
+
+ - Starting up an extra copy of an rsync daemon will not clobber the pidfile
+ for the running daemon -- if the pidfile exists, the new daemon will exit
+ with an error. This means that your wrapper script that starts the rsync
+ daemon should be made to handle lock-breaking (if you want any automatic
+ breaking of locks to be done).
+
+ BUG FIXES:
+
+ - A daemon with "use chroot = no" and excluded items listed in the daemon
+ config file now properly checks an absolute-path arg specified for these
+ options: --compare-dest, --link-dest, --copy-dest, --partial-dir,
+ --backup-dir, --temp-dir, and --files-from.
+
+ - A daemon can now be told to disable all user- and group-name translation
+ on a per-module basis. This avoids a potential problem with a writable
+ daemon module that has "use chroot" enabled -- if precautions weren't
+ taken, a user could try to add a missing library and get rsync to use
+ it. This makes rsync safer by default, and more configurable when id-
+ translation is not desired. See the daemon's "numeric ids" parameter
+ for full details.
+
+ - A chroot daemon can now indicate which part of its path should affect the
+ chroot call, and which part should become an inside-chroot path for the
+ module. This allows you to have outside-the-transfer paths (such as for
+ libraries) even when you enable chroot protection. The idiom used in the
+ rsyncd.conf file is: path = /chroot/dirs/./dirs/inside
+
+ - If a file's data arrived successfully on the receiving side but the
+ rename of the temporary file to the destination file failed AND the
+ --remove-source-files (or the deprecated --remove-sent-files) option
+ was specified, rsync no longer erroneously removes the associated
+ source file.
+
+ - Fixed the output of -ii when combined with one of the --*-dest options:
+ it now itemizes all the items, not just the changed ones.
+
+ - Made the output of all file types consistent when using a --*-dest
+ option. Prior versions would output too many creation events for
+ matching items.
+
+ - The code that waits for a child pid now handles being interrupted by a
+ signal. This fixes a problem with the pre-xfer exec function not being
+ able to get the exit status from the script.
+
+ - A negated filter rule (i.e. with a '!' modifier) no longer loses the
+ negation when sending the filter rules to the remote rsync.
+
+ - Fixed a problem with the --out-format (aka --log-format) option %f: it
+ no longer outputs superfluous directory info for a non-daemon rsync.
+
+ - Fixed a problem with -vv (double --verbose) and --stats when "pushing"
+ files (which includes local copies). Version 2.6.9 would complete the
+ copy, but exit with an error when the receiver output its memory stats.
+
+ - If --password-file is used on a non-daemon transfer, rsync now complains
+ and exits. This should help users figure out that they can't use this
+ option to control a remote shell's password prompt.
+
+ - Make sure that directory permissions of a newly-created destination
+ directory are handled right when --perms is left off.
+
+ - The itemized output of a newly-created destination directory is now
+ output as a creation event, not a change event.
+
+ - Improved --hard-link so that more corner cases are handled correctly
+ when combined with options such as --link-dest and/or --ignore-existing.
+
+ - The --append option no longer updates a file that has the same size.
+
+ - Fixed a bug when combining --backup and --backup-dir with --inplace:
+ any missing backup directories are now created.
+
+ - Fixed a bug when using --backup and --inplace with --whole-file or
+ --read-batch: backup files are actually created now.
+
+ - The daemon pidfile is checked and created sooner in the startup sequence.
+
+ - If a daemon module's "path" value is not an absolute pathname, the code
+ now makes it absolute internally (making it work properly).
+
+ - Ensure that a temporary file always has owner-write permission while we
+ are writing to it. This avoids problems with some network filesystems
+ when transfering read-only files.
+
+ - Any errors output about password-file reading no longer cause an error at
+ the end of the run about a partial transfer.
+
+ - The --read-batch option for protocol 30 now ensures that several more
+ options are set correctly for the current batch file: --iconv, --acls,
+ --xattrs, --inplace, --append, and --append-verify.
+
+ - Using --only-write-batch to a daemon receiver now works properly (older
+ versions would update some files while writing the batch).
+
+ - Avoid outputting a "file has vanished" message when the file is a broken
+ symlink and --copy-unsafe-links or --copy-dirlinks is used (the code
+ already handled this for --copy-links).
+
+ - Fixed the combination of --only-write-batch and --dry-run.
+
+ - Fixed rsync's ability to remove files that are not writable by the file's
+ owner when rsync is running as the same user.
+
+ - When transferring large files, the sender's hashtable of checksums is
+ kept at a more reasonable state of fullness (no more than 80% full) so
+ that the scanning of the hashtable will not bog down as the number of
+ blocks increases.
+
+ ENHANCEMENTS:
+
+ - A new incremental-recursion algorithm is now used when rsync is talking
+ to another 3.x version. This starts the transfer going more quickly
+ (before all the files have been found), and requires much less memory.
+ See the --recursive option in the manpage for some restrictions.
+
+ - Lowered memory use in the non-incremental-recursion algorithm for typical
+ option values (usually saving from 21-29 bytes per file).
+
+ - The default --delete algorithm is now --delete-during when talking to a
+ 3.x rsync. This is a faster scan than using --delete-before (which is
+ the default when talking to older rsync versions), and is compatible with
+ the new incremental recursion mode.
+
+ - Rsync now allows multiple remote-source args to be specified rather than
+ having to rely on a special space-splitting side-effect of the remote-
+ shell. Additional remote args must specify the same host or an empty one
+ (e.g. empty: :file1 or ::module/file2). For example, this means that
+ local use of brace expansion now works: rsync -av host:dir/{f1,f2} .
+
+ - Added the --protect-args (-s) option, that tells rsync to send most of
+ the command-line args at the start of the transfer rather than as args
+ to the remote-shell command. This protects them from space-splitting,
+ and only interprets basic wildcard special shell characters (*?[).
+
+ - Added the --delete-delay option, which is a more efficient way to delete
+ files at the end of the transfer without needing a separate delete pass.
+
+ - Added the --acls (-A) option to preserve Access Control Lists. This is
+ an improved version of the prior patch that was available, and it even
+ supports OS X ACLs. If you need to have backward compatibility with old,
+ ACL-patched versions of rsync, apply the acls.diff file from the patches
+ dir.
+
+ - Added the --xattrs (-X) option to preserve extended attributes. This is
+ an improved version of the prior patch that was available, and it even
+ supports OS X xattrs (which includes their resource fork data). If you
+ need to have backward compatibility with old, xattr-patched versions of
+ rsync, apply the xattrs.diff file from the patches dir.
+
+ - Added the --fake-super option that allows a non-super user to preserve
+ all attributes of a file by using a special extended-attribute idiom.
+ It even supports the storing of foreign ACL data on your backup server.
+ There is also an analogous "fake super" parameter for an rsync daemon.
+
+ - Added the --iconv option, which allows rsync to convert filenames from
+ one character-set to another during the transfer. The default is to
+ make this feature available as long as your system has iconv_open().
+ If compilation fails, specify --disable-iconv to configure, and then
+ rebuild. If you want rsync to perform character-set conversions by
+ default, you can specify --enable-iconv=CONVERT_STRING with the default
+ value for the --iconv option that you wish to use. For example,
+ "--enable-iconv=." is a good choice. See the rsync manpage for an
+ explanation of the --iconv option's settings.
+
+ - A new daemon config parameter, "charset", lets you control the character-
+ set that is used during an --iconv transfer to/from a daemon module. You
+ can also set your daemon to refuse "no-iconv" if you want to force the
+ client to use an --iconv transfer (requiring an rsync 3.x client).
+
+ - Added the --skip-compress=LIST option to override the default list of
+ file suffixes that will not be compressed when using --compress (-z).
+
+ - The daemon's default for "dont compress" was extended to include:
+ *.7z *.mp[34] *.mov *.avi *.ogg *.jpg *.jpeg
+ The name-matching routine was also optimized to run more quickly.
+
+ - The --max-delete option now outputs a warning if it skipped any file
+ deletions, including a count of how many deletions were skipped. (Older
+ versions just silently stopped deleting things.)
+
+ - You may specify --max-delete=0 to a 3.0.0 client to request that it warn
+ about extraneous files without deleting anything. If you're not sure
+ what version the client is, you can use the less-obvious --max-delete=-1,
+ as both old and new versions will treat that as the same request (though
+ older versions don't warn).
+
+ - The --hard-link option now uses less memory on both the sending and
+ receiving side for all protocol versions. For protocol 30, the use of a
+ hashtable on the sending side allows us to more efficiently convey to the
+ receiver what files are linked together. This reduces the amount of data
+ sent over the socket by a considerable margin (rather than adding more
+ data), and limits the in-memory storage of the device+inode information
+ to just the sending side for the new protocol 30, or to the receiving
+ side when speaking an older protocol (note that older rsync versions kept
+ the device+inode information on both sides).
+
+ - The filter rules now support a perishable ("p") modifier that marks rules
+ that should not have an effect in a directory that is being deleted. e.g.
+ -f '-p .svn/' would only affect "live" .svn directories.
+
+ - Rsync checks all the alternate-destination args for validity (e.g.
+ --link-dest). This lets the user know when they specified a directory
+ that does not exist.
+
+ - If we get an ENOSYS error setting the time on a symlink, we don't
+ complain about it anymore (for those systems that even support the
+ setting of the modify-time on a symlink).
+
+ - Protocol 30 now uses MD5 checksums instead of MD4.
+
+ - Changed the --append option to not checksum the existing data in the
+ destination file, which speeds up file appending.
+
+ - Added the --append-verify option, which works like the older --append
+ option (verifying the existing data in the destination file). For
+ compatibility with older rsync versions, any use of --append that is
+ talking protocol 29 or older will revert to the --append-verify method.
+
+ - Added the --contimeout=SECONDS option that lets the user specify a
+ connection timeout for rsync daemon access.
+
+ - Documented and extended the support for the RSYNC_CONNECT_PROG variable
+ that can be used to enhance the client side of a daemon connection.
+
+ - Improved the dashes and double-quotes in the nroff manpage output.
+
+ - Rsync now supports a lot more --no-OPTION override options.
+
+ INTERNAL:
+
+ - The file-list sorting algorithm now uses a sort that keeps any same-
+ named items in the same order as they were specified. This allows
+ rsync to always ensure that the first of the duplicates is the one
+ that will be included in the copy. The new sort is also faster
+ than the glibc version of qsort() and mergesort().
+
+ - Rsync now supports the transfer of 64-bit timestamps (time_t values).
+
+ - Made the file-deletion code use a little less stack when recursing
+ through a directory hierarchy of extraneous files.
+
+ - Fixed a build problem with older (2.x) versions of gcc.
+
+ - Added some isType() functions that make dealing with signed characters
+ easier without forcing variables via casts.
+
+ - Changed strcat/strcpy/sprintf function calls to use safer versions.
+
+ - Upgraded the included popt version to 1.10.2 and improved its use of
+ string-handling functions.
+
+ - Added missing prototypes for compatibility functions from the lib dir.
+
+ - Configure determines if iconv() has a const arg, allowing us to avoid a
+ compiler warning.
+
+ - Made the sending of some numbers more efficient for protocol 30.
+
+ - Make sure that a daemon process doesn't mind if the client was weird and
+ omitted the --server option.
+
+ - There are more internal logging categories available in protocol 30 than
+ the age-old FINFO and FERROR, including FERROR_XFER and FWARN. These new
+ categories allow some errors and warnings to go to stderr without causing
+ an erroneous end-of-run warning about some files not being able to be
+ transferred.
+
+ - Improved the use of "const" on pointers.
+
+ - Improved J.W.'s pool_alloc routines to add a way of incrementally freeing
+ older sections of a pool's memory.
+
+ - The getaddrinfo.c compatibility code in the "lib" dir was replaced with
+ some new code (derived from samba, derived from PostgreSQL) that has a
+ better license than the old code.
+
+ DEVELOPER RELATED:
+
+ - Rsync is now licensed under the GPLv3 or later.
+
+ - Rsync is now being maintained in a "git" repository instead of CVS
+ (though the old CVS repository still exists for historical access).
+ Several maintenance scripts were updated to work with git.
+
+ - Generated files are no longer committed into the source repository. The
+ autoconf and autoheader commands are now automatically run during the
+ normal use of "configure" and "make". The latest dev versions of all
+ generated files can also be copied from the samba.org web site (see the
+ prepare-source script's fetch option).
+
+ - The "patches" directory of diff files is now built from branches in the
+ rsync git repository (branch patch/FOO creates file patches/FOO.diff).
+ This directory is now distributed in a separate separate tar file named
+ rsync-patches-VERSION.tar.gz instead of the main rsync-VERSION.tar.gz.
+
+ - The proto.h file is now built using a simple perl script rather than a
+ complex awk script, which proved to be more widely compatible.
+
+ - When running the tests, we now put our per-test temp dirs into a sub-
+ directory named testtmp (which is created, if missing). This allows
+ someone to symlink the testtmp directory to another filesystem (which is
+ useful if the build dir's filesystem does not support ACLs and xattrs,
+ but another filesystem does).
+
+ - Rsync now has a way of handling protocol-version changes during the
+ development of a new protocol version. This causes any out-of-sync
+ versions to speak an older protocol rather than fail in a cryptic manner.
+ This addition makes it safer to deploy a pre-release version that may
+ interact with the public. This new exchange of sub-version info does not
+ interfere with the {MIN,MAX}_PROTOCOL_VERSION checking algorithm (which
+ does not have enough range to allow the main protocol number to be
+ incremented for every minor tweak in that happens during development).
+
+ - The csprotocol.txt file was updated to mention the daemon protocol change
+ in the 3.0.0 release.
+
+
+NEWS for rsync 2.6.9 (6 Nov 2006)
+Protocol: 29 (unchanged)
+Changes since 2.6.8:
+
+ BUG FIXES:
+
+ - If rsync is interrupted via a handled signal (such as SIGINT), it will
+ once again clean-up its temp file from the destination dir.
+
+ - Fixed an overzealous sanitizing bug in the handling of the --link-dest,
+ --copy-dest, and --compare-dest options to a daemon without chroot: if
+ the copy's destination dir is deeper than the top of the module's path,
+ these options now accept a safe number of parent-dir (../) references
+ (since these options are relative to the destination dir). The old code
+ incorrectly chopped off all "../" prefixes for these options, no matter
+ how deep the destination directory was in the module's hierarchy.
+
+ - Fixed a bug where a deferred info/error/log message could get sent
+ directly to the sender instead of being handled by rwrite() in the
+ generator. This fixes an "unexpected tag 3" fatal error, and should
+ also fix a potential problem where a deferred info/error message from
+ the receiver might bypass the log file and get sent only to the client
+ process. (These problems could only affect an rsync daemon that was
+ receiving files.)
+
+ - Fixed a bug when --inplace was combined with a --*-dest option and we
+ update a file's data using an alternate basis file. The code now
+ notices that it needs to copy the matching data from the basis file
+ instead of (wrongly) assuming that it was already present in the file.
+
+ - Fixed a bug where using --dry-run with a --*-dest option with a path
+ relative to a directory that does not yet exist: the affected option
+ gets its proper path value so that the output of the dry-run is right.
+
+ - Fixed a bug in the %f logfile escape when receiving files: the
+ destination path is now included in the output (e.g. you can now tell
+ when a user specifies a subdir inside a module).
+
+ - If the receiving side fails to create a directory, it will now skip
+ trying to update everything that is inside that directory.
+
+ - If --link-dest is specified with --checksum but without --times, rsync
+ will now allow a hard-link to be created to a matching link-dest file
+ even when the file's modify-time doesn't match the server's file.
+
+ - The daemon now calls more timezone-using functions prior to doing a
+ chroot. This should help some C libraries to generate proper timestamps
+ from inside a chrooted daemon (and to not try to access /etc/timezone
+ over and over again).
+
+ - Fixed a bug in the handling of an absolute --partial-dir=ABS_PATH option:
+ it now deletes an alternate basis file from the partial-dir that was used
+ to successfully update a destination file.
+
+ - Fixed a bug in the handling of --delete-excluded when using a per-dir
+ merge file: the merge file is now honored on the receiving side, and
+ only its unqualified include/exclude commands are ignored (just as is
+ done for global include/excludes).
+
+ - Fixed a recent bug where --delete was not working when transferring from
+ the root (/) of the filesystem with --relative enabled.
+
+ - Fixed a recent bug where an --exclude='*' could affect the root (/) of
+ the filesystem with --relative enabled.
+
+ - When --inplace creates a file, it is now created with owner read/write
+ permissions (0600) instead of no permissions at all. This avoids a
+ problem continuing a transfer that was interrupted (since --inplace
+ will not update a file that has no write permissions).
+
+ - If either --remove-source-files or --remove-sent-files is enabled and we
+ are unable to remove the source file, rsync now outputs an error.
+
+ - Fixed a bug in the daemon's "incoming chmod" rule: newly-created
+ directories no longer get the 'F' (file) rules applied to them.
+
+ - Fixed an infinite loop bug when a filter rule was rejected due to being
+ overly long.
+
+ - When the server receives a --partial-dir option from the client, it no
+ longer runs the client-side code that adds an assumed filter rule (since
+ the client will be sending us the rules in the usual manner, and they
+ may have chosen to override the auto-added rule).
+
+ ENHANCEMENTS:
+
+ - Added the --log-file=FILE and --log-file-format=FORMAT options. These
+ can be used to tell any rsync to output what it is doing to a log file.
+ They work with a client rsync, a non-daemon server rsync (see the man
+ page for instructions), and also allows the overriding of rsyncd.conf
+ settings when starting a daemon.
+
+ - The --log-format option was renamed to be --out-format to avoid confusing
+ it with affecting the log-file output. (The old option remains as an
+ alias for the new to preserve backward compatibility.)
+
+ - Made "log file" and "syslog facility" settable on a per-module basis in
+ the daemon's config file.
+
+ - Added the --remove-source-files option as a replacement for the (now
+ deprecated) --remove-sent-files option. This new option removes all
+ non-dirs from the source directories, even if the file was already
+ up-to-date. This fixes a problem where interrupting an rsync that
+ was using --remove-sent-files and restarting it could leave behind
+ a file that the earlier rsync synchronized, but didn't get to remove.
+ (The deprecated --remove-sent-files is still understood for now, and
+ still behaves in the same way as before.)
+
+ - Added the option --no-motd to suppress the message-of-the-day output
+ from a daemon when doing a copy. (See the manpage for a caveat.)
+
+ - Added a new environment variable to the pre-/post-xfer exec commands (in
+ the daemon's config file): RSYNC_PID. This value will be the same in
+ both the pre- and post-xfer commands, so it can be used as a unique ID
+ if the pre-xfer command wants to cache some arg/request info for the
+ post-xfer command.
+
+ INTERNAL:
+
+ - Did a code audit using IBM's code-checker program and made several
+ changes, including: replacing most of the strcpy() and sprintf()
+ calls with strlcpy(), snprintf(), and memcpy(), adding a 0-value to
+ an enum that had been intermingling a literal 0 with the defined enum
+ values, silencing some uninitialized memory checks, marking some
+ functions with a "noreturn" attribute, and changing an "if" that
+ could never succeed on some platforms into a pre-processor directive
+ that conditionally compiles the code.
+
+ - Fixed a potential bug in f_name_cmp() when both the args are a
+ top-level "." dir (which doesn't happen in normal operations).
+
+ - Changed exit_cleanup() so that it can never return instead of exit.
+ The old code might return if it found the exit_cleanup() function
+ was being called recursively. The new code is segmented so that
+ any recursive calls move on to the next step of the exit-processing.
+
+ - The macro WIFEXITED(stat) will now be defined if the OS didn't already
+ define it.
+
+ DEVELOPER RELATED:
+
+ - The acls.diff and xattrs.diff patches have received a bunch of work to
+ make them much closer to being acceptable in the main distribution.
+ The xattrs patch also has some preliminary Mac OS X and FreeBSD
+ compatibility code that various system types to exchange extended
+ file-attributes.
+
+ - A new diff in the patches dir, fake-root.diff, allows rsync to
+ maintain a backup hierarchy with full owner, group, and device info
+ without actually running as root. It does this using a special
+ extended attribute, so it depends on xattrs.diff (which depends on
+ acls.diff).
+
+ - The rsync.yo and rsyncd.conf.yo files have been updated to work
+ better with the latest yodl 2.x releases.
+
+ - Updated config.guess and config.sub to their 2006-07-02 versions.
+
+ - Updated various files to include the latest FSF address and to have
+ consistent opening comments.
+
+
+NEWS for rsync 2.6.8 (22 Apr 2006)
+Protocol: 29 (unchanged)
+Changes since 2.6.7:
+
+ BUG FIXES:
+
+ - Fixed a bug in the exclude code where an anchored exclude without any
+ wildcards fails to match an absolute source arg, but only when --relative
+ is in effect.
+
+ - Improved the I/O code for the generator to fix a potential hang when the
+ receiver gets an EOF on the socket but the generator's select() call
+ never indicates that the socket is writable for it to be notified about
+ the EOF. (This can happen when using stunnel).
+
+ - Fixed a problem with the file-reading code where a failed read (such as
+ that caused by a bad sector) would not advance the file's read-position
+ beyond the failed read's data.
+
+ - Fixed a logging bug where the "log file" directive was not being honored
+ in a single-use daemon (one spawned by a remote-shell connection or by
+ init).
+
+ - If rsync cannot honor the --delete option, we output an error and exit
+ instead of silently ignoring the option.
+
+ - Fixed a bug in the --link-dest code that prevented special files (such as
+ fifos) from being linked.
+
+ - The ability to hard-link symlinks and special files is now determined at
+ configure time instead of at runtime. This fixes a bug with --link-dest
+ creating a hard-link to a symlink's referent on a BSD system.
+
+ ENHANCEMENTS:
+
+ - In daemon mode, if rsync fails to bind to the requested port, the
+ error(s) returned by socket() and/or bind() are now logged.
+
+ - When we output a fatal error, we now output the version of rsync in the
+ message.
+
+ - Improved the documentation for the --owner and --group options.
+
+ - The rsyncstats script in "support" has an improved line-parsing regex
+ that is easier to read and also makes it to parse syslog-generated lines.
+
+ - A new script in "support": file-attr-restore, can be used to restore the
+ attributes of a file-set (the permissions, ownership, and group info)
+ taken from the cached output of a "find ARG... -ls" command.
+
+ DEVELOPER RELATED:
+
+ - Removed the unused function write_int_named(), the unused variable
+ io_read_phase, and the rarely used variable io_write_phase. This also
+ elides the confusing 'phase "unknown"' part of one error message.
+
+ - Removed two unused configure checks and two related (also unused)
+ compatibility functions.
+
+ - The xattrs.diff patch received a security fix that prevents a potential
+ buffer overflow in the receive_xattr() code.
+
+ - The acls.diff patch has been improved quite a bit, with more to come.
+
+ - A new patch was added: log-file.diff. This contains an early version of
+ a future option, --log-file=FILE, that will allow any rsync to log its
+ actions to a file (something that only a daemon supports at present).
+
+
+NEWS for rsync 2.6.7 (11 Mar 2006)
+Protocol: 29 (unchanged)
+Changes since 2.6.6:
+
+ OUTPUT CHANGES:
+
+ - The letter 'D' in the itemized output was being used for both devices
+ (character or block) as well as other special files (such as fifos and
+ named sockets). This has changed to separate non-device special files
+ under the 'S' designation (e.g. "cS+++++++ path/fifo"). See also the
+ "--specials" option, below.
+
+ - The way rsync escapes unreadable characters has changed. First, rsync
+ now has support for recognizing valid multibyte character sequences in
+ your current locale, allowing it to escape fewer characters than before
+ for a locale such as UTF-8. Second, it now uses an escape idiom of
+ "\#123", which is the literal string "\#" followed by exactly 3 octal
+ digits. Rsync no longer doubles a backslash character in a filename
+ (e.g. it used to output "foo\\bar" when copying "foo\bar") -- now it only
+ escapes a backslash that is followed by a hash-sign and 3 digits (0-9)
+ (e.g. it will output "foo\#134#789" when copying "foo\#789"). See also
+ the --8-bit-output (-8) option, mentioned below.
+
+ Script writers: the local rsync is the one that outputs escaped names,
+ so if you need to support unescaping of filenames for older rsyncs, I'd
+ suggest that you parse the output of "rsync --version" and only use the
+ old unescaping rules for 2.6.5 and 2.6.6.
+
+ BUG FIXES:
+
+ - Fixed a really old bug that caused --checksum (-c) to checksum all the
+ files encountered during the delete scan (ouch).
+
+ - Fixed a potential hang in a remote generator: when the receiver gets a
+ read-error on the socket, it now signals the generator about this so that
+ the generator does not try to send any of the terminating error messages
+ to the client (avoiding a potential hang in some setups).
+
+ - Made hard-links work with symlinks and devices again.
+
+ - If the sender gets an early EOF reading a source file, we propagate this
+ error to the receiver so that it can discard the file and try requesting
+ it again (which is the existing behavior for other kinds of read errors).
+
+ - If a device-file/special-file changes permissions, rsync now updates the
+ permissions without recreating the file.
+
+ - If the user specifies a remote-host for both the source and destination,
+ we now output a syntax error rather than trying to open the destination
+ hostspec as a filename.
+
+ - When --inplace creates a new destination file, rsync now creates it with
+ permissions 0600 instead of 0000 -- this makes restarting possible when
+ the transfer gets interrupted in the middle of sending a new file.
+
+ - Reject the combination of --inplace and --sparse since the sparse-output
+ algorithm doesn't work when overwriting existing data.
+
+ - Fixed the directory name in the error that is output when pop_dir()
+ fails.
+
+ - Really fixed the parsing of a "!" entry in .cvsignore files this time.
+
+ - If the generator gets a stat() error on a file, output it (this used to
+ require at least -vv for the error to be seen).
+
+ - If waitpid() fails or the child rsync didn't exit cleanly, we now handle
+ the exit status properly and generate a better error.
+
+ - Fixed some glitches in the double-verbose output when using --copy-dest,
+ --link-dest, or --compare-dest. Also improved how the verbose output
+ handles hard-links (within the transfer) that had an up-to-date alternate
+ "dest" file, and copied files (via --copy-dest).
+
+ - Fixed the matching of the dont-compress items (e.g. *.gz) against files
+ that have a path component containing a slash.
+
+ - If the code reading a filter/exclude file gets an EINTR error, rsync now
+ clears the error flag on the file handle so it can keep on reading.
+
+ - If --relative is active, the sending side cleans up trailing "/" or "/."
+ suffixes to avoid triggering a bug in older rsync versions. Also, we now
+ reject a ".." dir if it would be sent as a relative dir.
+
+ - If a non-directory is in the way of a directory and rsync is run with
+ --dry-run and --delete, rsync no longer complains about not being able
+ to opendir() the not-yet present directory.
+
+ - When --list-only is used and a non-existent local destination dir was
+ also specified as a destination, rsync no longer generates a warning
+ about being unable to create the missing directory.
+
+ - Fixed some problems with --relative --no-implied-dirs when the
+ destination directory did not yet exist: we can now create a symlink or
+ device when it is the first thing in the missing dir, and --fuzzy no
+ longer complains about being unable to open the missing dir.
+
+ - Fixed a bug where the --copy-links option would not affect implied
+ directories without --copy-unsafe-links (see --relative).
+
+ - Got rid of the need for --force to be used in some circumstances with
+ --delete-after (making it consistent with --delete-before/-during).
+
+ - Rsync now ignores the SIGXFSZ signal, just in case your OS sends this
+ when a file is too large (rsync handles the write error).
+
+ - Fixed a bug in the Proxy-Authorization header's base64-encoded value: it
+ was not properly padded with trailing '=' chars. This only affects a
+ user that need to use a password-authenticated proxy for an outgoing
+ daemon-rsync connection.
+
+ - If we're transferring an empty directory to a new name, rsync no longer
+ forces S_IWUSR if it wasn't already set, nor does it accidentally leave
+ it set.
+
+ - Fixed a bug in the debug output (-vvvvv) that could mention the wrong
+ checksum for the current file offset.
+
+ - Rsync no longer allows a single directory to be copied over a non-
+ directory destination arg.
+
+ ENHANCEMENTS:
+
+ - Added the --append option that makes rsync append data onto files that
+ are longer on the source than the destination (this includes new files).
+
+ - Added the --min-size=SIZE option to exclude small files from the
+ transfer.
+
+ - Added the --compress-level option to allow you to set how aggressive
+ rsync's compression should be (this option implies --compress).
+
+ - Enhanced the parsing of the SIZE value for --min-size and --max-size to
+ allow easy entry of multiples of 1000 (instead of just multiples of 1024)
+ and off-by-one values too (e.g. --max-size=8mb-1).
+
+ - Added the --8-bit-output (-8) option, which tells rsync to avoid escaping
+ high-bit characters that it thinks are unreadable in the current locale.
+
+ - The new option --human-readable (-h) changes the output of --progress,
+ --stats, and the end-of-run summary to be easier to read. If repeated,
+ the units become powers of 1024 instead of powers of 1000. (The old
+ meaning of -h, as a shorthand for --help, still works as long as you
+ just use it on its own, as in "rsync -h".)
+
+ - If lutimes() and/or lchmod() are around, use them to allow the
+ preservation of attributes on symlinks.
+
+ - The --link-dest option now affects symlinks and devices (when possible).
+
+ - Added two config items to the rsyncd.conf parsing: "pre-xfer exec" and
+ "post-xfer exec". These allow a command to be specified on a per-module
+ basis that will be run before and/or after a daemon-mode transfer. (See
+ the man page for a list of the environment variables that are set with
+ information about the transfer.)
+
+ - When using the --relative option, you can now insert a dot dir in
+ the source path to indicate where the replication of the source dirs
+ should start. For example, if you specify a source path of
+ rsync://host/module/foo/bar/./baz/dir with -R, rsync will now only
+ replicate the "baz/dir" part of the source path (note: a trailing
+ dot dir is unaffected unless it also has a trailing slash).
+
+ - Added some new --no-FOO options that make it easier to override unwanted
+ implied or default options. For example, "-a --no-o" (aka "--archive
+ --no-owner") can be used to turn off the preservation of file ownership
+ that is implied by -a.
+
+ - Added the --chmod=MODE option that allows the destination permissions to
+ be changed from the source permissions. E.g. --chmod=g+w,o-rwx
+
+ - Added the "incoming chmod" and "outgoing chmod" daemon options that allow
+ a module to specify what permissions changes should be applied to all
+ files copied to and from the daemon.
+
+ - Allow the --temp-dir option to be specified when starting a daemon, which
+ sets the default temporary directory for incoming files.
+
+ - If --delete is combined with --dirs without --recursive, rsync will now
+ delete in any directory whose content is being synchronized.
+
+ - If --backup is combined with --delete without --backup-dir (and without
+ --delete-excluded), we add a "protect" filter-rule to ensure that files
+ with the backup suffix are not deleted.
+
+ - The file-count stats that are output by --progress were improved to
+ better indicate what the numbers mean. For instance, the output:
+ "(xfer#5, to-check=8383/9999)" indicates that this was the fifth file
+ to be transferred, and we still need to check 8383 more files out of
+ a total of 9999.
+
+ - The include/exclude code now allows a dir/*** directive (with 3 trailing
+ stars) to match both the dir itself as well as all the content below the
+ dir (dir/** would not match the dir).
+
+ - Added the --prune-empty-dirs (-m) option that makes the receiving rsync
+ discard empty chains of directories from the file-list. This makes it
+ easier to selectively copy files from a source hierarchy and end up with
+ just the directories needed to hold the resulting files.
+
+ - If the --itemize-changes (-i) option is repeated, rsync now includes
+ unchanged files in the itemized output (similar to -vv, but without all
+ the other verbose messages that can get in the way). Of course, the
+ client must be version 2.6.7 for this to work, but the remote rsync only
+ needs to be 2.6.7 if you're pushing files.
+
+ - Added the --specials option to tell rsync to copy non-device special
+ files (which rsync now attempts even as a normal user). The --devices
+ option now requests the copying of just devices (character and block).
+ The -D option still requests both (e.g. --devices and --specials), -a
+ still implies -D, and non-root users still get a silent downgrade that
+ omits device copying.
+
+ - Added the --super option to make the receiver always attempt super-user
+ activities. This is useful for systems that allow things such as devices
+ to be created or ownership to be set without being UID 0, and is also
+ useful for someone who wants to ensure that errors will be output if the
+ receiving rsync isn't being run as root.
+
+ - Added the --sockopts option for those few who want to customize the TCP
+ options used to contact a daemon rsync.
+
+ - Added a way for the --temp-dir option to be combined with a partial-dir
+ setting that lets rsync avoid non-atomic updates (for those times when
+ --temp-dir is not being used because space is tight).
+
+ - A new support script, files-to-excludes, will transform a list of files
+ into a set of include/exclude directives that will copy those files.
+
+ - A new option, --executability (-E) can be used to preserve just the
+ execute bit on files, for those times when using the --perms option is
+ not desired.
+
+ - The daemon now logs each connection and also each module-list request
+ that it receives.
+
+ - New log-format options: %M (modtime), %U (uid), %G (gid), and %B
+ (permission bits, e.g. "rwxr-xrwt").
+
+ - The --dry-run option no longer forces the enabling of --verbose.
+
+ - The --remove-sent-files option now does a better job of incrementally
+ removing the sent files on the sending side (older versions tended to
+ clump up all the removals at the end).
+
+ - A daemon now supersedes its minimal SIGCHLD handler with the standard
+ PID-remembering version after forking. This ensures that the generator
+ can get the child-exit status from the receiver.
+
+ - Use of the --bwlimit option no longer interferes with the remote rsync
+ sending error messages about invalid/refused options.
+
+ - Rsync no longer returns a usage error when used with one local source arg
+ and no destination: this now implies the --list-only option, just like
+ the comparable situation with a remote source arg.
+
+ - Added the --copy-dirlinks option, a more limited version of --copy-links.
+
+ - Various documentation improvements, including: a better synopsis, some
+ improved examples, a better discussion of the presence and absence of
+ --perms (including how it interacts with the new --executability and
+ --chmod options), an extended discussion of --temp-dir, an improved
+ discussion of --partial-dir, a better description of rsync's pattern
+ matching characters, an improved --no-implied-dirs section, and the
+ documenting of what the --stats option outputs.
+
+ - Various new and updated diffs in the patches dir, including: acls.diff,
+ xattrs.diff, atimes.diff, detect-renamed.diff, and slp.diff.
+
+ INTERNAL:
+
+ - We now use sigaction() and sigprocmask() if possible, and fall back on
+ signal() if not. Using sigprocmask() ensures that rsync enables all the
+ signals that it needs, just in case it was started in a masked state.
+
+ - Some buffer sizes were expanded a bit, particularly on systems where
+ MAXPATHLEN is overly small (e.g. cygwin).
+
+ - If io_printf() tries to format more data than fits in the buffer, exit
+ with an error instead of transmitting a truncated buffer.
+
+ - If a va_copy macro is defined, lib/snprintf.c will use it when defining
+ the VA_COPY macro.
+
+ - Reduced the amount of stack memory needed for each level of directory
+ recursion by nearly MAXPATHLEN bytes.
+
+ - The wildmatch function was extended to allow an array of strings to be
+ supplied as the string to match. This allows the exclude code to do less
+ string copying.
+
+ - Got rid of the safe_fname() function (and all the myriad calls) and
+ replaced it with a new function in the log.c code that filters all the
+ output going to the terminal.
+
+ - Unified the f_name() and the f_name_to() functions.
+
+ - Improved the hash-table code the sender uses to handle checksums to make
+ it use slightly less memory and run just a little faster.
+
+ DEVELOPER RELATED:
+
+ - The diffs in the patches dir now require "patch -p1 high in clean_flist() was wrong for an empty list.
+ This could cause flist_find() to crash in certain rare circumstances
+ (e.g. if just the right directory setup was around when --fuzzy was
+ combined with --link-dest).
+
+ - The outputting of hard-linked files when verbosity was > 1 was not right:
+ (1) Without -i it would output the name of each hard-linked file as
+ though it had been changed; it now outputs a "is hard linked" message for
+ the file. (2) With -i it would output all dots for the unchanged
+ attributes of a hard-link; it now changes those dots to spaces, as is
+ done for other totally unchanged items.
+
+ - When backing up a changed symlink or device, get rid of any old backup
+ item so that we don't get an "already exists" error.
+
+ - A couple places that were comparing a local and a remote modification-
+ time were not honoring the --modify-window option.
+
+ - Fixed a bug where the 'p' (permissions) itemized-changes flag might get
+ set too often (if some non-significant mode bits differed).
+
+ - Fixed a really old, minor bug that could cause rsync to warn about being
+ unable to mkdir() a path that ends in "/." because it just created the
+ directory (required --relative, --no-implied-dirs, a source path that
+ ended in either a trailing slash or a trailing "/.", and a non-existing
+ destination dir to tickle the bug in a recent version).
+
+ ENHANCEMENTS:
+
+ - Made the "max verbosity" setting in the rsyncd.conf file settable on a
+ per-module basis (which now matches the documentation).
+
+ - The support/rrsync script has been upgraded to verify the args of options
+ that take args (instead of rejecting any such options). The script was
+ also changed to try to be more secure and to fix a problem in the parsing
+ of a pull operation that has multiple sources.
+
+ - Improved the documentation that explains the difference between a
+ normal daemon transfer and a daemon-over remote-shell transfer.
+
+ - Some of the diffs supplied in the patches dir were fixed and/or
+ improved.
+
+ BUILD CHANGES:
+
+ - Made configure define NOBODY_USER (currently hard-wired to "nobody") and
+ NOBODY_GROUP (set to either "nobody" or "nogroup" depending on what we
+ find in the /etc/group file).
+
+ - Added a test to the test suite, itemized.test, that tests the output of
+ -i (log-format w/%i) and some double-verbose messages.
+
+
+NEWS for rsync 2.6.5 (1 Jun 2005)
+Protocol: 29 (unchanged)
+Changes since 2.6.4:
+
+ OUTPUT CHANGES:
+
+ - Non-printable chars in filenames are now output using backslash-
+ escaped characters rather than '?'s. Any non-printable character is
+ output using 3 digits of octal (e.g. "\n" -> "\012"), and a backslash
+ is now output as "\\". Rsync also uses your locale setting, which
+ can make it treat fewer high-bit characters as non-printable.
+
+ - If rsync received an empty file-list when pulling files, it would
+ output a "nothing to do" message and exit with a 0 (success) exit
+ status, even if the remote rsync returned an error (it did not do
+ this under the same conditions when pushing files). This was changed
+ to make the pulling behavior the same as the pushing behavior: we
+ now do the normal end-of-run outputting (depending on options) and
+ exit with the appropriate exit status.
+
+ BUG FIXES:
+
+ - A crash bug was fixed when a daemon had its "path" set to "/", did
+ not have chroot enabled, and used some anchored excludes in the
+ rsyncd.conf file.
+
+ - Fixed a bug in the transfer of a single file when -H is specified
+ (rsync would either infinite loop or perhaps crash).
+
+ - Fixed a case where the generator might try (and fail) to tweak the
+ write-permissions of a read-only directory in list-only mode (this
+ only caused an annoying warning message).
+
+ - If --compare-dest or --link-dest uses a locally-copied file as the
+ basis for an updated version, log this better when --verbose or -i
+ is in effect.
+
+ - Fixed the accidental disabling of --backup during the --delete-after
+ processing.
+
+ - Restored the ability to use the --address option in client mode (in
+ addition to its use in daemon mode).
+
+ - Make sure that some temporary progress information from the delete
+ processing does not get left on the screen when it is followed by a
+ newline.
+
+ - When --existing skips a directory with extra verbosity, refer to it
+ as a "directory", not a "file".
+
+ - When transferring a single file to a different-named file, any
+ generator messages that are source-file related no longer refer to
+ the file by the destination filename.
+
+ - Fixed a bug where hard-linking a group of files might fail if the
+ generator hasn't created a needed destination directory yet.
+
+ - Fixed a bug where a hard-linked group of files that is newly-linked
+ to a file in a --link-dest dir doesn't link the files from the rest
+ of the cluster.
+
+ - When deleting files with the --one-file-system (-x) option set, rsync
+ no longer tries to remove files from inside a mount-point on the
+ receiving side. Also, we don't complain about being unable to remove
+ the mount-point dir.
+
+ - Fixed a compatibility problem when using --cvs-ignore (-C) and
+ sending files to an older rsync without using --delete.
+
+ - Make sure that a "- !" or "+ !" include/exclude pattern does not
+ trigger the list-clearing action that is reserved for "!".
+
+ - Avoid a timeout in the generator when the sender/receiver aren't
+ handling the generator's checksum output quickly enough.
+
+ - Fixed the omission of some directories in the delete processing when
+ --relative (-R) was combined with a source path that had a trailing
+ slash.
+
+ - Fixed a case where rsync would erroneously delete some files and then
+ re-transfer them when the options --relative (-R) and --recursive
+ (-r) were both enabled (along with --delete) and a source path had a
+ trailing slash.
+
+ - Make sure that --max-size doesn't affect a device or a symlink.
+
+ - Make sure that a system with a really small MAXPATHLEN does not cause
+ the buffers in readfd_unbuffered() to be too small to receive normal
+ messages. (This mainly affected Cygwin.)
+
+ - If a source pathname ends with a filename of "..", treat it as if
+ "../" had been specified (so that we don't copy files to the parent
+ dir of the destination).
+
+ - If --delete is combined with a file-listing rsync command (i.e. no
+ transfer is happening), avoid outputting a warning that we couldn't
+ delete anything.
+
+ - If --stats is specified with --delete-after, ensure that all the
+ "deleting" messages are output before the statistics.
+
+ - Improved one "if" in the deletion code that was only checking errno
+ for ENOTEMPTY when it should have also been checking for EEXIST (for
+ compatibility with OS variations).
+
+ ENHANCEMENTS:
+
+ - Added the --only-write-batch=FILE option that may be used (instead
+ of --write-batch=FILE) to create a batch file without doing any
+ actual updating of the destination. This allows you to divert all
+ the file-updating data away from a slow data link (as long as you
+ are pushing the data to the remote server when creating the batch).
+
+ - When the generator is taking a long time to fill up its output buffer
+ (e.g. if the transferred files are few, small, or missing), it now
+ periodically flushes the output buffer so that the sender/receiver
+ can get started on the files sooner rather than later.
+
+ - Improved the keep-alive code to handle a long silence between the
+ sender and the receiver that can occur when the sender is receiving
+ the checksum data for a large file.
+
+ - Improved the auth-errors that are logged by the daemon to include
+ some information on why the authorization failed: wrong user,
+ password mismatch, etc. (The client-visible message is unchanged!)
+
+ - Improved the client's handling of an "@ERROR" from a daemon so that
+ it does not complain about an unexpectedly closed socket (since we
+ really did expect the socket to close).
+
+ - If the daemon can't open the log-file specified in rsyncd.conf, fall
+ back to using syslog and log an appropriate warning. This is better
+ than what was typically a totally silent (and fatal) failure (since a
+ daemon is not usually run with the --no-detach option that was
+ necessary to see the error on stderr).
+
+ - The man pages now consistently refer to an rsync daemon as a "daemon"
+ instead of a "server" (to distinguish it from the server process in a
+ non-daemon transfer).
+
+ - Made a small change to the rrsync script (restricted rsync -- in the
+ support dir) to make a read-only server reject all --remove-* options
+ when sending files (to future-proof it against the possibility of
+ other similar options being added at some point).
+
+ INTERNAL:
+
+ - Rsync now calls setlocale(LC_CTYPE, ""). This enables isprint() to
+ better discern which filename characters need to be escaped in
+ messages (which should result in fewer escaped characters in some
+ locales).
+
+ - Improved the naming of the log-file open/reopen/close functions.
+
+ - Removed some protocol-compatibility code that was only needed to help
+ someone running a pre-release of 2.6.4.
+
+ BUILD CHANGES:
+
+ - Added configure option "--disable-locale" to disable any use of
+ setlocale() in the binary.
+
+ - Fixed a bug in the SUPPORT{,_HARD}_LINKS #defines which prevented
+ rsync from being built without symlink or hard-link support.
+
+ - Only #define HAVE_REMSH if it is going to be set to 1.
+
+ - Configure now disables the use of mkstemp() under HP-UX (since they
+ refuse to fix its broken handling of large files).
+
+ - Configure now explicitly checks for the lseek64() function so that
+ the code can use HAVE_LSEEK64 instead of inferring lseek64()'s
+ presence based on the presence of the off64_t type.
+
+ - Configure no longer mentions the change in the default remote-shell
+ (from rsh to ssh) that occurred for the 2.6.0 release.
+
+ - Some minor enhancements to the test scripts.
+
+ - Added a few new *.diff files to the patches dir, including a patch
+ that enables the optional copying of extended attributes.
+
+
+NEWS for rsync 2.6.4 (30 March 2005)
+Protocol: 29 (changed)
+Changes since 2.6.3:
+
+ OUTPUT CHANGES:
+
+ - When rsync deletes a directory and outputs a verbose message about
+ it, it now appends a trailing slash to the name instead of (only
+ sometimes) outputting a preceding "directory " string.
+
+ - The --stats output will contain file-list time-statistics if both
+ sides are 2.6.4, or if the local side is 2.6.4 and the files are
+ being pushed (since the stats come from the sending side).
+ (Requires protocol 29 for a pull.)
+
+ - The "%o" (operation) log-format escape now has a third value (besides
+ "send" and "recv"): "del." (with trailing dot to make it 4 chars).
+ This changes the way deletions are logged in the daemon's log file.
+
+ - When the --log-format option is combined with --verbose, rsync now
+ avoids outputting the name of the file twice in most circumstances.
+ As long as the --log-format item does not refer to any post-transfer
+ items (such as %b or %c), the --log-format message is output prior to
+ the transfer, so --verbose is now the equivalent of a --log-format of
+ '%n%L' (which outputs the name and any link info). If the log output
+ must occur after the transfer to be complete, the only time the name
+ is also output prior to the transfer is when --progress was specified
+ (so that the name will precede the progress stats, and the full
+ --log-format output will come after).
+
+ - Non-printable characters in filenames are replaced with a '?' to
+ avoid corrupting the screen or generating empty lines in the output.
+
+ BUG FIXES:
+
+ - Restore the list-clearing behavior of "!" in a .cvsignore file (2.6.3
+ was only treating it as a special token in an rsync include/exclude
+ file).
+
+ - The combination of --verbose and --dry-run now mentions the full list
+ of changes that would be output without --dry-run.
+
+ - Avoid a mkdir warning when removing a directory in the destination
+ that already exists in the --backup-dir.
+
+ - An OS that has a binary mode for its files (such as cygwin) needed
+ setmode(fd, O_BINARY) called on the temp-file we opened with
+ mkstemp(). (Fix derived from cygwin's 2.6.3 rsync package.)
+
+ - Fixed a potential hang when verbosity is high, the client side is
+ the sender, and the file-list is large.
+
+ - Fixed a potential protocol-corrupting bug where the generator could
+ merge a message from the receiver into the middle of a multiplexed
+ packet of data if only part of that data had been written out to the
+ socket when the message from the generator arrived.
+
+ - We now check if the OS doesn't support using mknod() for creating
+ FIFOs and sockets, and compile-in some compatibility code using
+ mkfifo() and socket() when necessary.
+
+ - Fixed an off-by-one error in the handling of --max-delete=N. Also,
+ if the --max-delete limit is exceeded during a run, we now output a
+ warning about this at the end of the run and exit with a new error
+ code (25).
+
+ - One place in the code wasn't checking if fork() failed.
+
+ - The "ignore nonreadable" daemon parameter used to erroneously affect
+ readable symlinks that pointed to a non-existent file.
+
+ - If the OS does not have lchown() and a chown() of a symlink will
+ affect the referent of a symlink (as it should), we no longer try
+ to set the user and group of a symlink.
+
+ - The generator now properly runs the hard-link loop and the dir-time
+ rewriting loop after we're sure that the redo phase is complete.
+
+ - When --backup was specified with --partial-dir=DIR, where DIR is a
+ relative path, the backup code was erroneously trying to backup a
+ file that was put into the partial-dir.
+
+ - If a file gets resent in a single transfer and the --backup option is
+ enabled along with --inplace, rsync no longer performs a duplicate
+ backup (it used to overwrite the first backup with the failed file).
+
+ - One call to flush_write_file() was not being checked for an error.
+
+ - The --no-relative option was not being sent from the client to a
+ server sender.
+
+ - If an rsync daemon specified "dont compress = ..." for a file and the
+ client tried to specify --compress, the libz code was not handling a
+ compression level of 0 properly. This could cause a transfer failure
+ if the block-size for a file was large enough (e.g. rsync might have
+ exited with an error for large files).
+
+ - Fixed a bug that would sometimes surface when using --compress and
+ sending a file with a block-size larger than 64K (either manually
+ specified, or computed due to the file being really large). Prior
+ versions of rsync would sometimes fail to decompress the data
+ properly, and thus the transferred file would fail its verification.
+
+ - If a daemon can't open the specified log file (i.e. syslog is not
+ being used), die without crashing. We also output an error about
+ the failure on stderr (which will only be seen if --no-detach was
+ specified) and exit with a new error code (6).
+
+ - A local transfer no longer duplicates all its include/exclude options
+ (since the forked process already has a copy of the exclude list,
+ there's no need to send them a set of duplicates).
+
+ - The output of the items that are being updated by the generator (dirs,
+ symlinks, devices) is now intermingled in the proper order with the
+ output from the items that the receiver is updating (regular files)
+ when pulling. This misordering was particularly bad when --progress
+ was specified. (Requires protocol 29.)
+
+ - When --timeout is specified, lulls that occur in the transfer while
+ the generator is doing work that does not generate socket traffic
+ (looking for changed files, deleting files, doing directory-time
+ touch-ups, etc.) will cause a new keep-alive packet to be sent that
+ should keep the transfer going as long as the generator continues to
+ make progress. (Requires protocol 29.)
+
+ - The stat size of a device is not added to the total file size of the
+ items in the transfer (the size might be undefined on some OSes).
+
+ - Fixed a problem with refused-option messages sometimes not making it
+ back to the client side when a remote --files-from was in effect and
+ the daemon was the receiver.
+
+ - The --compare-dest option was not updating a file that differed in
+ (the preserved) attributes from the version in the compare-dest DIR.
+
+ - When rsync is copying files into a write-protected directory, fixed
+ the change-report output for the directory so that we don't report
+ an identical directory as changed.
+
+ ENHANCEMENTS:
+
+ - Rsync now supports popt's option aliases, which means that you can
+ use /etc/popt and/or ~/.popt to create your own option aliases.
+
+ - Added the --delete-during (--del) option which will delete files
+ from the receiving side incrementally as each directory in the
+ transfer is being processed. This makes it more efficient than the
+ default, before-the-transfer behavior, which is now also available as
+ --delete-before (and is still the default --delete-WHEN option that
+ will be chosen if --delete or --delete-excluded is specified without
+ a --delete-WHEN choice). All the --del* options infer --delete, so
+ an rsync daemon that refuses "delete" will still refuse to allow any
+ file-deleting options (including the new --remove-sent-files option).
+
+ - All the --delete-WHEN options are now more memory efficient:
+ Previously an duplicate set of file-list objects was created on the
+ receiving side for the entire destination hierarchy. The new
+ algorithm only creates one directory of objects at a time (for files
+ inside the transfer).
+
+ - Added the --copy-dest option, which works like --link-dest except
+ that it locally copies identical files instead of hard-linking them.
+
+ - Added support for specifying multiple --compare-dest, --copy-dest, or
+ --link-dest options, but only of a single type. (Promoted from the
+ patches dir and enhanced.) (Requires protocol 29.)
+
+ - Added the --max-size option. (Promoted from the patches dir.)
+
+ - The daemon-mode options are now separated from the normal rsync
+ options so that they can't be mixed together. This makes it
+ impossible to start a daemon that has improper default option values
+ (which could cause problems when a client connects, such as hanging
+ or crashing).
+
+ - The --bwlimit option may now be used in combination with --daemon
+ to specify both a default value for the daemon side and a value
+ that cannot be exceeded by a user-specified --bwlimit option.
+
+ - Added the "port" parameter to the rsyncd.conf file. (Promoted from
+ the patches dir.) Also added "address". The command-line options
+ take precedence over a config-file option, as expected.
+
+ - In _exit_cleanup(): when we are exiting with a partially-received
+ file, we now flush any data in the write-cache before closing the
+ partial file.
+
+ - The --inplace support was enhanced to work with --compare-dest,
+ --link-dest, and (the new) --copy-dest options. (Requires protocol
+ 29.)
+
+ - Added the --dirs (-d) option for an easier way to copy directories
+ without recursion. Any directories that are encountered are created
+ on the destination. Specifying a directory with a trailing slash
+ copies its immediate contents to the destination.
+
+ - The --files-from option now implies --dirs (-d).
+
+ - Added the --list-only option, which is mainly a way for the client to
+ put the server into listing mode without needing to resort to any
+ internal option kluges (e.g. the age-old use of "-r --exclude="/*/*"
+ for a non-recursive listing). This option is used automatically
+ (behind the scenes) when a modern rsync speaks to a modern daemon,
+ but may also be specified manually if you want to force the use of
+ the --list-only option over a remote-shell connection.
+
+ - Added the --omit-dir-times (-O) option, which will avoid updating
+ the modified time for directories when --times was specified. This
+ option will avoid an extra pass through the file-list at the end of
+ the transfer (to tweak all the directory times), which may provide
+ an appreciable speedup for a really large transfer. (Promoted from
+ the patches dir.)
+
+ - Added the --filter (-f) option and its helper option, -F. Filter
+ rules are an extension to the existing include/exclude handling
+ that also supports nested filter files as well as per-directory
+ filter files (like .cvsignore, but with full filter-rule parsing).
+ This new option was chosen in order to ensure that all existing
+ include/exclude processing remained 100% compatible with older
+ versions. Protocol 29 is needed for full filter-rule support, but
+ backward-compatible rules work with earlier protocol versions.
+ (Promoted from the patches dir and enhanced.)
+
+ - Added the --delay-updates option that puts all updated files into
+ a temporary directory (by default ".~tmp~", but settable via the
+ --partial-dir=DIR option) until the end of the transfer. This
+ makes the updates a little more atomic for a large transfer.
+
+ - If rsync is put into the background, any output from --progress is
+ reduced.
+
+ - Documented the "max verbosity" setting for rsyncd.conf. (This
+ setting was added a couple releases ago, but left undocumented.)
+
+ - The sender and the generator now double-check the file-list index
+ they are given, and refuse to try to do a file transfer on a
+ non-file index (since that would indicate that something had gone
+ very wrong).
+
+ - Added the --itemize-changes (-i) option, which is a way to output a
+ more detailed list of what files changed and in what way. The effect
+ is the same as specifying a --log-format of "%i %n%L" (see both the
+ rsync and rsyncd.conf manpages). Works with --dry-run too.
+
+ - Added the --fuzzy (-y) option, which attempts to find a basis file
+ for a file that is being created from scratch. The current algorithm
+ only looks in the destination directory for the created file, but it
+ does attempt to find a match based on size/mod-time (in case the file
+ was renamed with no other changes) as well as based on a fuzzy
+ name-matching algorithm. This option requires protocol 29 because it
+ needs the new file-sorting order. (Promoted from patches dir and
+ enhanced.) (Requires protocol 29.)
+
+ - Added the --remove-sent-files option, which lets you move files
+ between systems.
+
+ - The hostname in HOST:PATH or HOST::PATH may now be an IPv6 literal
+ enclosed in '[' and ']' (e.g. "[::1]"). (We already allowed IPv6
+ literals in the rsync://HOST:PORT/PATH format.)
+
+ - When rsync recurses to build the file list, it no longer keeps open
+ one or more directory handles from the dir's parent dirs.
+
+ - When building under windows, the default for --daemon is now to
+ avoid detaching, requiring the new --detach option to force rsync
+ to detach.
+
+ - The --dry-run option can now be combined with either --write-batch or
+ --read-batch, allowing you to run a do-nothing test command to see
+ what would happen without --dry-run.
+
+ - The daemon's "read only" config item now sets an internal read_only
+ variable that makes extra sure that no write/delete calls on the
+ read-only side can succeed.
+
+ - The log-format % escapes can now have a numeric field width in
+ between the % and the escape letter (e.g. "%-40n %08p").
+
+ - Improved the option descriptions in the --help text.
+
+ SUPPORT FILES:
+
+ - Added atomic-rsync to the support dir: a perl script that will
+ transfer some files using rsync, and then move the updated files into
+ place all at once at the end of the transfer. Only works when
+ pulling, and uses --link-dest and a parallel hierarchy of files to
+ effect its update.
+
+ - Added mnt-excl to the support dir: a perl script that takes the
+ /proc/mounts file and translates it into a set of excludes that will
+ exclude all mount points (even mapped mounts to the same disk). The
+ excludes are made relative to the specified source dir and properly
+ anchored.
+
+ - Added savetransfer.c to the support dir: a C program that can make
+ a copy of all the data that flows over the wire. This lets you test
+ for data corruption (by saving the data on both the sending side and
+ the receiving side) and provides one way to debug a protocol error.
+
+ - Added rrsync to the support dir: this is an updated version of Joe
+ Smith's restricted rsync perl script. This helps to ensure that only
+ certain rsync commands can be run by an ssh invocation.
+
+ INTERNAL:
+
+ - Added better checking of the checksum-header values that come over
+ the socket.
+
+ - Merged a variety of file-deleting functions into a single function so
+ that it is easier to maintain.
+
+ - Improved the type of some variables (particularly blocksize vars) for
+ consistency and proper size.
+
+ - Got rid of the uint64 type (which we didn't need).
+
+ - Use a slightly more compatible set of core #include directives.
+
+ - Defined int32 in a way that ensures that the build dies if we can't
+ find a variable with at least 32 bits.
+
+ PROTOCOL DIFFERENCES FOR VERSION 29:
+
+ - A 16-bit flag-word is transmitted after every file-list index. This
+ indicates what is changing between the sender and the receiver. The
+ generator now transmits an index and a flag-word to indicate when
+ dirs and symlinks have changed (instead of producing a message),
+ which makes the outputting of the information more consistent and
+ less prone to screen corruption (because the local receiver/sender is
+ now outputting all the file-change info messages).
+
+ - If a file is being hard-linked, the ITEM_XNAME_FOLLOWS bit is enabled
+ in the flag-word and the name of the file that was linked immediately
+ follows in vstring format (see below).
+
+ - If a file is being transferred with an alternate-basis file, the
+ ITEM_BASIS_TYPE_FOLLOWS bit is enabled in the flag-word and a single
+ byte follows, indicating what type of basis file was chosen. If that
+ indicates that a fuzzy-match was selected, the ITEM_XNAME_FOLLOWS bit
+ is set in the flag-word and the name of the match in vstring format
+ follows the basis byte. A vstring is a variable length string that
+ has its size written prior to the string, and no terminating null.
+ If the string is from 1-127 bytes, the length is a single byte. If
+ it is from 128-32767 bytes, the length is written as ((len >> 8) |
+ 0x80) followed by (len % 0x100).
+
+ - The sending of exclude names is done using filter-rule syntax. This
+ means that all names have a prefixed rule indicator, even excludes
+ (which used to be sent as a bare pattern, when possible). The -C
+ option will include the per-dir .cvsignore merge file in the list of
+ filter rules so it is positioned correctly (unlike in some older
+ transfer scenarios).
+
+ - Rsync sorts the filename list in a different way: it sorts the subdir
+ names after the non-subdir names for each dir's contents, and it
+ always puts a dir's contents immediately after the dir's name in the
+ list. (Previously an item named "foo.txt" would sort in between
+ directory "foo/" and "foo/bar".)
+
+ - When talking to a protocol 29 rsync daemon, a list-only request
+ is able to note this before the options are sent over the wire and
+ the new --list-only option is included in the options.
+
+ - When the --stats bytes are sent over the wire (or stored in a batch),
+ they now include two elapsed-time values: one for how long it took to
+ build the file-list, and one for how long it took to send it over the
+ wire (each expressed in thousandths of a second).
+
+ - When --delete-excluded is specified with some filter rules (AKA
+ excludes), a client sender will now initiate a send of the rules to
+ the receiver (older protocols used to omit the sending of excludes in
+ this situation since there were no receiver-specific rules that
+ survived --delete-excluded back then). Note that, as with all the
+ filter-list sending, only items that are significant to the other
+ side will actually be sent over the wire, so the filter-rule list
+ that is sent in this scenario is often empty.
+
+ - An index equal to the file-list count is sent as a keep-alive packet
+ from the generator to the sender, which then forwards it on to the
+ receiver. This normally invalid index is only a valid keep-alive
+ packet if the 16-bit flag-word that follows it contains a single bit
+ (ITEM_IS_NEW, which is normally an illegal flag to appear alone).
+
+ - A protocol-29 batch file includes a bit for the setting of the --dirs
+ option and for the setting of the --compress option. Also, the shell
+ script created by --write-batch will use the --filter option instead
+ of --exclude-from to capture any filter rules.
+
+ BUILD CHANGES:
+
+ - Handle an operating system that use mkdev() in place of makedev().
+
+ - Improved configure to better handle cross-compiling.
+
+
+NEWS for rsync 2.6.3 (30 Sep 2004)
+Protocol: 28 (unchanged)
+Changes since 2.6.2:
+
+ SECURITY FIXES:
+
+ - A bug in the sanitize_path routine (which affects a non-chrooted
+ rsync daemon) could allow a user to craft a pathname that would get
+ transformed into an absolute path for certain options (but not for
+ file-transfer names). If you're running an rsync daemon with chroot
+ disabled, *please upgrade*, ESPECIALLY if the user privs you run
+ rsync under is anything above "nobody".
+
+ OUTPUT CHANGES (ATTN: those using a script to parse the verbose output):
+
+ - Please note that the 2-line footer (output when verbose) now uses the
+ term "sent" instead of "wrote" and "received" instead of "read". If
+ you are not parsing the numeric values out of this footer, a script
+ would be better off using the empty line prior to the footer as the
+ indicator that the verbose output is over.
+
+ - The output from the --stats option was similarly affected to change
+ "written" to "sent" and "read" to "received".
+
+ - Rsync ensures that a filename that contains a newline gets mentioned
+ with each newline transformed into a question mark (which prevents a
+ filename from causing an empty line to be output).
+
+ - The "backed up ..." message that is output when at least 2 --verbose
+ options are specified is now the same both with and without the
+ --backup-dir option.
+
+ BUG FIXES:
+
+ - Fixed a crash bug that might appear when --delete was used and
+ multiple source directories were specified.
+
+ - Fixed a 32-bit truncation of the file length when generating the
+ checksums.
+
+ - The --backup code no longer attempts to create some directories
+ over and over again (generating warnings along the way).
+
+ - Fixed a bug in the reading of the secrets file (by the daemon) and
+ the password file (by the client): the files no longer need to be
+ terminated by a newline for their content to be read in.
+
+ - If a file has a read error on the sending side or the reconstructed
+ data doesn't match the expected checksum (perhaps due to the basis
+ file changing during the transfer), the receiver will no longer
+ retain the resulting file unless the --partial option was specified.
+ (Note: for the read-error detection to work, neither side can be
+ older than 2.6.3 -- older receivers will always retain the file, and
+ older senders don't tell the receiver that the file had a read
+ error.)
+
+ - If a file gets resent in a single transfer and the --backup option
+ is enabled, rsync no longer performs a duplicate backup (it used to
+ overwrite the original file in the backup area).
+
+ - Files specified in the daemon's "exclude" or "exclude from" config
+ items are now excluded from being uploaded (assuming that the module
+ allows uploading at all) in addition to the old download exclusion.
+
+ - Got rid of a potential hang in the receiver when near the end of a
+ phase.
+
+ - When using --backup without a --backup-dir, rsync no longer preserves
+ the modify time on directories. This avoids confusing NFS.
+
+ - When --copy-links (-L) is specified, we now output a separate error
+ for a symlink that has no referent instead of claiming that a file
+ "vanished".
+
+ - The --copy-links (-L) option no longer has the side-effect of telling
+ the receiving side to follow symlinks. See the --keep-dirlinks
+ option (mentioned below) for a way to specify that behavior.
+
+ - Error messages from the daemon server's option-parsing (such as
+ refused options) are now successfully transferred back to the client
+ (the server used to fail to send the message because the socket
+ wasn't in the right state for the message to get through).
+
+ - Most transfer errors that occur during a daemon transfer are now
+ returned to the user in addition to being logged (some messages are
+ intended to be daemon-only and are not affected by this).
+
+ - Fixed a bug in the daemon authentication code when using one of the
+ batch-processing options.
+
+ - We try to work around some buggy IPv6 implementations that fail to
+ implement IPV6_V6ONLY. This should fix the "address in use" error
+ that some daemons get when running on an OS with a buggy IPv6
+ implementation. Also, if the new code gets this error, we might
+ suggest that the user specify --ipv4 or --ipv6 (if we think it will
+ help).
+
+ - When the remote rsync dies, make a better effort to recover any error
+ messages it may have sent before dying (the local rsync used to just
+ die with a socket-write error).
+
+ - When using --delete and a --backup-dir that contains files that are
+ hard-linked to their destination equivalents, rsync now makes sure
+ that removed files really get removed (avoids a really weird rename()
+ behavior).
+
+ - Avoid a bogus run-time complaint about a lack of 64-bit integers when
+ the int64 type is defined as an off_t and it actually has 64-bits.
+
+ - Added a configure check for open64() without mkstemp64() so that we
+ can avoid using mkstemp() when such a combination is encountered.
+ This bypasses a problem writing out large temp files on OSes such as
+ AIX and HP-UX.
+
+ - Fixed an age-old crash problem with --read-batch on a local copy
+ (rsync was improperly assuming --whole-file for the local copy).
+
+ - When --dry-run (-n) is used and the destination directory does not
+ exist, rsync now produces a correct report of files that would be
+ sent instead of dying with a chdir() error.
+
+ - Fixed a bug that could cause a slow-to-connect rsync daemon to die
+ with an error instead of waiting for the connection to finish.
+
+ - Fixed an ssh interaction that could cause output to be lost when the
+ user chose to combine the output of rsync's stdout and stderr (e.g.
+ using the "2>&1").
+
+ - Fixed an option-parsing bug when --files-from got passed to a daemon.
+
+ ENHANCEMENTS:
+
+ - Added the --partial-dir=DIR option that lets you specify where to
+ (temporarily) put a partially transferred file (instead of over-
+ writing the destination file). E.g. --partial-dir=.rsync-partial
+ Also added support for the RSYNC_PARTIAL_DIR environment variable
+ that, when found, transforms a regular --partial option (such as
+ the convenient -P option) into one that also specifies a directory.
+
+ - Added --keep-dirlinks (-K), which allows you to symlink a directory
+ onto another partition on the receiving side and have rsync treat it
+ as matching a normal directory from the sender.
+
+ - Added the --inplace option that tells rsync to write each destination
+ file without using a temporary file. The matching of existing data
+ in the destination file can be severely limited by this, but there
+ are also cases where this is more efficient (such as appending data).
+ Use only when needed (see the man page for more details).
+
+ - Added the "write only" option for the daemon's config file.
+
+ - Added long-option names for -4 and -6 (namely --ipv4 and --ipv6)
+ and documented all these options in the man page.
+
+ - Improved the handling of the --bwlimit option so that it's less
+ bursty, more accurate, and works properly over a larger range of
+ values.
+
+ - The rsync daemon-over-ssh code now looks for SSH_CONNECTION and
+ SSH2_CLIENT in addition to SSH_CLIENT to figure out the IP address.
+
+ - Added the --checksum-seed=N option for advanced users.
+
+ - Batch writing/reading has a brand-new implementation that is simpler,
+ fixes a few weird problems with the old code (such as no longer
+ sprinkling the batch files into different dirs or even onto different
+ systems), and is much less intrusive into the code (making it easier
+ to maintain for the future). The new code generates just one data
+ file instead of three, which makes it possible to read the batch on
+ stdin via a remote shell. Also, the old requirement of forcing the
+ same fixed checksum-seed for all batch processing has been removed.
+
+ - If an rsync daemon has a module set with "list = no" (which hides its
+ presence in the list of available modules), a user that fails to
+ authenticate gets the same "unknown module" error that they would get
+ if the module were actually unknown (while still logging the real
+ error to the daemon's log file). This prevents fishing for module
+ names.
+
+ - The daemon's "refuse options" config item now allows you to match
+ option names using wildcards and/or the single-letter option names.
+
+ - Each transferred file now gets its permissions and modified-time
+ updated before the temp-file gets moved into place. Previously, the
+ finished file would have a very brief window where its permissions
+ disallowed all group and world access.
+
+ - Added the ability to parse a literal IPv6 address in an "rsync:" URL
+ (e.g. rsync://[2001:638:500:101::21]:873/module/dir).
+
+ - The daemon's wildcard expanding code can now handle more than 1000
+ filenames (it's now limited by memory instead of having a hard-wired
+ limit).
+
+ INTERNAL:
+
+ - Some cleanup in the exclude code has saved some per-exclude memory
+ and made the code easier to maintain.
+
+ - Improved the argv-overflow checking for a remote command that has a
+ lot of args.
+
+ - Use rsyserr() in the various places that were still calling rprintf()
+ with strerror() as an arg.
+
+ - If an rsync daemon is listening on multiple sockets (to handle both
+ IPv4 and IPv6 to a single port), we now close all the unneeded file
+ handles after we accept a connection (we used to close just one of
+ them).
+
+ - Optimized the handling of larger block sizes (rsync used to slow to a
+ crawl if the block size got too large).
+
+ - Optimized away a loop in hash_search().
+
+ - Some improvements to the sanitize_path() and clean_fname() functions
+ makes them more efficient and produce better results (while still
+ being compatible with the file-name cleaning that gets done on both
+ sides when sending the file-list).
+
+ - Got rid of alloc_sanitize_path() after adding a destination-buffer
+ arg to sanitize_path() made it possible to put all the former's
+ functionality into the latter.
+
+ - The file-list that is output when at least 4 verbose options are
+ specified reports the uid value on the sender even when rsync is
+ not running as root (since we might be sending to a root receiver).
+
+ BUILD CHANGES:
+
+ - Added a "gen" target to rebuild most of the generated files,
+ including configure, config.h.in, the man pages, and proto.h.
+
+ - If "make proto" doesn't find some changes in the prototypes, the
+ proto.h file is left untouched (its time-stamp used to always be
+ updated).
+
+ - The variable $STRIP (that is optionally set by the install-strip
+ target's rule) was changed to $INSTALL_STRIP because some systems
+ have $STRIP already set in the environment.
+
+ - Fixed a build problem when SUPPORT_HARD_LINKS isn't defined.
+
+ - When cross-compiling, the gettimeofday() function is now assumed to
+ be a modern version that takes two-args (since we can't test it).
+
+ DEVELOPER RELATED:
+
+ - The scripts in the testsuite dir were cleaned up a bit and a few
+ new tests added.
+
+ - Some new diffs were added to the patches dir, and some accepted
+ ones were removed.
+
+
+NEWS for rsync 2.6.2 (30 Apr 2004)
+Protocol: 28 (unchanged)
+Changes since 2.6.1:
+
+ BUG FIXES:
+
+ - Fixed a major bug in the sorting of the filenames when --relative
+ is used for some sources (just sources such as "/" and "/*" were
+ affected). This fix ensures that we ask for the right file-list
+ item when requesting changes from the sender.
+
+ - Rsync now checks the return value of the close() function to
+ better report disk-full problems on an NFS file system.
+
+ - Restored the old daemon-server behavior of logging error messages
+ rather than returning them to the user. (A better long-term fix
+ will be sought in the future.)
+
+ - An obscure uninitialized-variable bug was fixed in the uid/gid
+ code. (This bug probably had no ill effects.)
+
+ BUILD CHANGES:
+
+ - Got rid of the configure check for sys/sysctl.h (it wasn't used
+ and was causing a problem on some systems). Also improved the
+ broken-largefile-locking test to try to avoid failure due to an
+ NFS build-dir.
+
+ - Fixed a compile problem on systems that don't define
+ AI_NUMERICHOST.
+
+ - Fixed a compile problem in the popt source for compilers that
+ don't support __attribute__.
+
+ DEVELOPER RELATED:
+
+ - Improved the testsuite's "merge" test to work on OSF1.
+
+ - Two new diffs were added to the patches dir.
+
+
+NEWS for rsync 2.6.1 (26 Apr 2004)
+Protocol: 28 (changed)
+Changes since 2.6.0:
+
+ SECURITY FIXES:
+
+ - Paths sent to an rsync daemon are more thoroughly sanitized when
+ chroot is not used. If you're running a non-read-only rsync
+ daemon with chroot disabled, *please upgrade*, ESPECIALLY if the
+ user privs you run rsync under is anything above "nobody".
+
+ ENHANCEMENTS:
+
+ - Lower memory use, more optimal transfer of data over the socket,
+ and lower CPU usage (see the INTERNAL section for details).
+
+ - The RSYNC_PROXY environment variable can now contain a
+ "USER:PASS@" prefix before the "HOST:PORT" information.
+ (Bardur Arantsson)
+
+ - The --progress output now mentions how far along in the transfer
+ we are, including both a count of files transferred and a
+ percentage of the total file-count that we've processed. It also
+ shows better current-rate-of-transfer and remaining-transfer-time
+ values.
+
+ - Documentation changes now attempt to describe some often mis-
+ understood features more clearly.
+
+ BUG FIXES:
+
+ - When -x (--one-file-system) is combined with -L (--copy-links) or
+ --copy-unsafe-links, no symlinked files are skipped, even if the
+ referent file is on a different filesystem.
+
+ - The --link-dest code now works properly for a non-root user when
+ (1) the UIDs of the source and destination differ and -o was
+ specified, or (2) when the group of the source can't be used on
+ the destination and -g was specified.
+
+ - Fixed a bug in the handling of -H (hard-links) that might cause
+ the expanded PATH/NAME value of the current item to get
+ overwritten (due to an expanded-name caching bug).
+
+ - We now reset the "new data has been sent" flag at the start of
+ each file we send. This makes sure that an interrupted transfer
+ with the --partial option set doesn't keep a shorter temp file
+ than the current basis file when no new data has been transferred
+ over the wire for that file.
+
+ - Fixed a byte-order problem in --batch-mode on big-endian machines.
+ (Jay Fenlason)
+
+ - When using --cvs-exclude, the exclude items we get from a
+ per-directory's .cvsignore file once again only affect that one
+ directory (not all following directories too). The items are also
+ now properly word-split and parsed without any +/- prefix parsing.
+
+ - When specifying the USER@HOST: prefix for a file, the USER part
+ can now contain an '@', if needed (i.e. the last '@' is used to
+ find the HOST, not the first).
+
+ - Fixed some bugs in the handling of group IDs for non-root users:
+ (1) It properly handles a group that the sender didn't have a name
+ for (it would previously skip changing the group on any files in
+ that group). (2) If --numeric-ids is used, rsync no longer
+ attempts to set groups that the user doesn't have the permission
+ to set.
+
+ - Fixed the "refuse options" setting in the rsyncd.conf file.
+
+ - Improved the -x (--one-file-system) flag's handling of any mount-
+ point directories we encounter. It is both more optimal (in that
+ it no longer does a useless scan of the contents of the mount-
+ point dirs) and also fixes a bug where a remapped mount of the
+ original filesystem could get discovered in a subdir we should be
+ ignoring.
+
+ - Rsync no longer discards a double-slash at the start of a filename
+ when trying to open the file. It also no longer constructs names
+ that start with a double slash (unless the user supplied them).
+
+ - Path-specifying options to a daemon should now work the same with
+ or without chroot turned on. Previously, such a option (such as
+ --link-dest) would get its absolute path munged into a relative
+ one if chroot was not on, making that setting fairly useless.
+ Rsync now transforms the path into one that is based on the
+ module's base dir when chroot is not enabled.
+
+ - Fixed a compatibility problem interacting with older rsync
+ versions that might send us an empty --suffix value without
+ telling us that --backup-dir was specified.
+
+ - The "hosts allow" option for a daemon-over-remote-shell process
+ now has improved support for IPv6 addresses and a fix for systems
+ that have a length field in their socket structs.
+
+ - Fixed the ability to request an empty backup --suffix when sending
+ files to an rsync daemon.
+
+ - Fixed an option-parsing bug when --files-from was sent to a server
+ sender.
+
+ INTERNAL:
+
+ - Most of the I/O is now buffered, which results in a pretty large
+ speedup when running under MS Windows. (Craig Barratt)
+
+ - Optimizations to the name-handling/comparing code have made some
+ significant reductions in user-CPU time for large file sets.
+
+ - Some cleanup of the variable types make the code more consistent.
+
+ - Reduced memory requirements of hard link preservation.
+ (J.W. Schultz)
+
+ - Implemented a new algorithm for hard-link handling that speeds up
+ the code significantly. (J.W. Schultz and Wayne Davison)
+
+ - The --hard-link option now uses the first existing file in the
+ group of linked files as the basis for the transfer. This
+ prevents the sub-optimal transfer of a file's data when a new
+ hardlink is added on the sending side and it sorts alphabetically
+ earlier in the list than the files that are already present on the
+ receiving side.
+
+ - Dropped support for protocol versions less than 20 (2.3.0 released
+ 15 Mar 1999) and activated warnings for protocols less than 25
+ (2.5.0 released 23 Aug 2001). (Wayne Davison and J.W. Schultz,
+ severally)
+
+ - More optimal data transmission for --hard-links (protocol 28).
+
+ - More optimal data transmission for --checksum (protocol 28).
+
+ - Less memory is used when --checksum is specified.
+
+ - Less memory is used in the file list (a per-file savings).
+
+ - The generator is now better about not modifying the file list
+ during the transfer in order to avoid a copy-on-write memory
+ bifurcation (on systems where fork() uses shared memory).
+ Previously, rsync's shared memory would slowly become unshared,
+ resulting in real memory usage nearly doubling on the receiving
+ side by the end of the transfer. Now, as long as permissions
+ are being preserved, the shared memory should remain that way
+ for the entire transfer.
+
+ - Changed hardlink info and file_struct + strings to use allocation
+ pools. This reduces memory use for large file-sets and permits
+ freeing memory to the OS. (J.W. Schultz)
+
+ - The 2 pipes used between the receiver and generator processes
+ (which are forked on the same machine) were reduced to 1 pipe and
+ the protocol improved so that (1) it is now impossible to have the
+ "redo" pipe fill up and hang rsync, and (2) trailing messages from
+ the receiver don't get lost on their way through the generator
+ over to the sender (which mainly affected hard-link messages and
+ verbose --stats output).
+
+ - Improved the internal uid/gid code to be more portable and a
+ little more optimized.
+
+ - The device numbers sent when using --devices are now sent as
+ separate major/minor values with 32-bit accuracy (protocol 28).
+ Previously, the copied devices were sent as a single 32-bit
+ number. This will make inter-operation of 64-bit binaries more
+ compatible with their 32-bit brethren (with both ends of the
+ connection are using protocol 28). Note that optimizations in the
+ binary protocol for sending the device numbers often results in
+ fewer bytes being used than before, even though more precision is
+ now available.
+
+ - Some cleanup of the exclude/include structures and its code made
+ things clearer (internally), simpler, and more efficient.
+
+ - The reading & writing of the file-list in batch-mode is now
+ handled by the same code that sends & receives the list over the
+ wire. This makes it much easier to maintain. (Note that the
+ batch code is still considered to be experimental.)
+
+ BUILD CHANGES:
+
+ - The configure script now accepts --with-rsyncd-conf=PATH to
+ override the default value of the /etc/rsyncd.conf file.
+
+ - Fixed configure bug when running "./configure --disable-ipv6".
+
+ - Fixed compilation problem on Tru64 Unix (having to do with
+ sockaddr.sa_len and sockaddr.sin_len).
+
+ DEVELOPER RELATED:
+
+ - Fixed "make test" bug when build dir is not the source dir.
+
+ - Added a couple extra diffs in the "patches" dir, removed the ones
+ that got applied, and rebuilt the rest.
+
+
+NEWS for rsync 2.6.0 (1 Jan 2004)
+Protocol: 27 (changed)
+Changes since 2.5.7:
+
+ ENHANCEMENTS:
+
+ * "ssh" is now the default remote shell for rsync. If you want to
+ change this, configure like this: "./configure --with-rsh=rsh".
+
+ * Added --files-from, --no-relative, --no-implied-dirs, and --from0.
+ Note that --from0 affects the line-ending character for all the
+ files read by the --*-from options. (Wayne Davison)
+
+ * Length of csum2 is now per-file starting with protocol version
+ 27. (J.W. Schultz)
+
+ * Per-file dynamic block size is now sqrt(file length). The
+ per-file checksum size is determined according to an algorithm
+ provided by Donovan Baarda which reduces the probability of rsync
+ algorithm corrupting data and falling back using the whole md4
+ checksums. (J.W. Schultz, Donovan Baarda)
+
+ * The --stats option no longer includes the (debug) malloc summary
+ unless the verbose option was specified at least twice.
+
+ * Added a new error/warning code for when files vanish from the
+ sending side. Made vanished source files not interfere with the
+ file-deletion pass when --delete-after was specified.
+
+ * Various trailing-info sections are now preceded by a newline.
+
+ BUG FIXES:
+
+ * Fixed several exclude/include matching bugs when using wild-cards.
+ This has a several user-visible effects, all of which make the
+ matching more consistent and intuitive. This should hopefully not
+ cause anyone problems since it makes the matching work more like
+ what people are expecting. (Wayne Davison)
+
+ - A pattern with a "**" no longer causes a "*" to match slashes.
+ For example, with "/*/foo/**", "foo" must be 2 levels deep.
+ [If your string has BOTH "*" and "**" wildcards, changing the
+ "*" wildcards to "**" will provide the old behavior in all
+ versions.]
+
+ - "**/foo" now matches at the base of the transfer (like /foo
+ does). [Use "/**/foo" to get the old behavior in all versions.]
+
+ - A non-anchored wildcard term floats to match beyond the base of
+ the transfer. E.g. "CVS/R*" matches at the end of the path,
+ just like the non-wildcard term "CVS/Root" does. [Use "/CVS/R*"
+ to get the old behavior in all versions.]
+
+ - Including a "**" in the match term causes it to be matched
+ against the entire path, not just the name portion, even if
+ there aren't any interior slashes in the term. E.g. "foo**bar"
+ would exclude "/path/foo-bar" (just like before) as well as
+ "/foo-path/baz-bar" (unlike before). [Use "foo*bar" to get the
+ old behavior in all versions.]
+
+ * The exclude list specified in the daemon's config file is now
+ properly applied to the pulled items no matter how deep the
+ user's file-args are in the source tree. (Wayne Davison)
+
+ * For protocol version >= 27, mdfour_tail() is called when the
+ block size (including checksum_seed) is a multiple of 64.
+ Previously it was not called, giving the wrong MD4 checksum.
+ (Craig Barratt)
+
+ * For protocol version >= 27, a 64 bit bit counter is used in
+ mdfour.c as required by the RFC. Previously only a 32 bit bit
+ counter was used, causing incorrect MD4 file checksums for
+ file sizes >= 512MB - 4. (Craig Barratt)
+
+ * Fixed a crash bug when interacting with older rsync versions and
+ multiple files of the same name are destined for the same dir.
+ (Wayne Davison)
+
+ * Keep tmp names from overflowing MAXPATHLEN.
+
+ * Make --link-dest honor the absence of -p, -o, and -g.
+
+ * Made rsync treat a trailing slash in the destination in a more
+ consistent manner.
+
+ * Fixed file I/O error detection. (John Van Essen)
+
+ * Fixed bogus "malformed address {hostname}" message in rsyncd log
+ when checking IP address against hostnames from "hosts allow"
+ and "hosts deny" parameters in config file.
+
+ * Print heap statistics when verbose >= 2 instead of when >= 1.
+
+ * Fixed a compression (-z) bug when syncing a mostly-matching file
+ that contains already-compressed data. (Yasuoka Masahiko and
+ Wayne Davison)
+
+ * Fixed a bug in the --backup code that could cause deleted files
+ to not get backed up.
+
+ * When the backup code makes new directories, create them with mode
+ 0700 instead of 0755 (since the directory permissions in the
+ backup tree are not yet copied from the main tree).
+
+ * Call setgroups() in a more portable manner.
+
+ * Improved file-related error messages to better indicate exactly
+ what pathname failed. (Wayne Davison)
+
+ * Fixed some bugs in the handling of --delete and --exclude when
+ using the --relative (-R) option. (Wayne Davison)
+
+ * Fixed bug that prevented regular files from replacing
+ special files and caused a directory in --link-dest or
+ --compare-dest to block the creation of a file with the
+ same path. A directory still cannot be replaced by a
+ regular file unless --delete specified. (J.W. Schultz)
+
+ * Detect and report when open or opendir succeed but read and
+ readdir fail caused by network filesystem issues and truncated
+ files. (David Norwood, Michael Brown, J.W. Schultz)
+
+ * Added a fix that should give ssh time to restore the tty settings
+ if the user presses Ctrl-C at an ssh password prompt.
+
+ INTERNAL:
+
+ * Eliminated vestigial support for old versions that we stopped
+ supporting. (J.W. Schultz)
+
+ * Simplified some of the option-parsing code. (Wayne Davison)
+
+ * Some cleanup made to the exclude code, as well as some new
+ defines added to enhance readability. (Wayne Davison)
+
+ * Changed the protocol-version code so that it can interact at a
+ lower protocol level than the maximum supported by both sides.
+ Added an undocumented option, --protocol=N, to force the value
+ we advertise to the other side (primarily for testing purposes).
+ (Wayne Davison)
+
+
+NEWS for rsync 2.5.7 (4 Dec 2003)
+Protocol: 26 (unchanged)
+Changes since 2.5.6:
+
+ SECURITY FIXES:
+
+ * Fix buffer handling bugs. (Andrew Tridgell, Martin Pool, Paul
+ Russell, Andrea Barisani)
+
+
+NEWS for rsync 2.5.6, aka "the dwd-between-jobs release" (26 Jan 2003)
+Protocol: 26 (unchanged)
+Changes since 2.5.5:
+
+ ENHANCEMENTS:
+
+ * The --delete-after option now implies --delete. (Wayne Davison)
+
+ * The --suffix option can now be used with --backup-dir. (Michael
+ Zimmerman)
+
+ * Combining "::" syntax with the --rsh/-e option now uses the
+ specified remote-shell as a transport to talk to a (newly-spawned)
+ server-daemon. This allows someone to use daemon features, such
+ as modules, over a secure protocol, such as ssh. (JD Paul)
+
+ * The rsync:// syntax for daemon connections is now accepted in the
+ destination field.
+
+ * If the file name given to --include-from or --exclude-from is "-",
+ rsync will read from standard input. (J.W. Schultz)
+
+ * New option --link-dest which is like --compare-dest except that
+ unchanged files are hard-linked in to the destination directory.
+ (J.W. Schultz)
+
+ * Don't report an error if an excluded file disappears during an
+ rsync run. (Eugene Chupriyanov and Bo Kersey)
+
+ * Added .svn to --cvs-exclude list to support subversion. (Jon
+ Middleton)
+
+ * Properly support IPv6 addresses in the rsyncd.conf "hosts allow"
+ and "hosts deny" fields. (Hideaki Yoshifuji)
+
+ * Changed exclude file handling to permit DOS or MAC style line
+ terminations. (J.W. Schultz)
+
+ * Ignore errors from chmod when -p/-a/--preserve-perms is not set.
+ (Dave Dykstra)
+
+ BUG FIXES:
+
+ * Fix "forward name lookup failed" errors on AIX 4.3.3. (John
+ L. Allen, Martin Pool)
+
+ * Generate each file's rolling-checksum data as we send it, not
+ in a separate (memory-eating) pass before hand. This prevents
+ timeout errors on really large files. (Stefan Nehlsen)
+
+ * Fix compilation on Tru64. (Albert Chin, Zoong Pham)
+
+ * Better handling of some client-server errors. (Martin Pool)
+
+ * Fixed a crash that would occur when sending a list of files that
+ contains a duplicate name (if it sorts to the end of the file
+ list) and using --delete. (Wayne Davison)
+
+ * Fixed the file-name duplicate-removal code when dealing with multiple
+ dups in a row. (Wayne Davison)
+
+ * Fixed a bug that caused rsync to lose the exit status of its child
+ processes and sometimes return an exit code of 0 instead of showing
+ an error. (David R. Staples, Dave Dykstra)
+
+ * Fixed bug in --copy-unsafe-links that caused it to be completely
+ broken. (Dave Dykstra)
+
+ * Prevent infinite recursion in cleanup code under certain circumstances.
+ (Sviatoslav Sviridov and Marc Espie)
+
+ * Fixed a bug that prevented rsync from creating intervening directories
+ when --relative-paths/-R is set. (Craig Barratt)
+
+ * Prevent "Connection reset by peer" messages from Cygwin. (Randy O'Meara)
+
+ INTERNAL:
+
+ * Many code cleanups and improved internal documentation. (Martin
+ Pool, Nelson Beebe)
+
+ * Portability fixes. (Dave Dykstra and Wayne Davison)
+
+ * More test cases. (Martin Pool)
+
+ * Some test-case fixes. (Brian Poole, Wayne Davison)
+
+ * Updated included popt to the latest vendor drop, version 1.6.4.
+ (Jos Backus)
+
+ * Updated config.guess and config.sub to latest versions; this
+ means rsync should build on more platforms. (Paul Green)
+
+
+NEWS for rsync 2.5.5, aka Snowy River (2 Apr 2002)
+Protocol: 26 (unchanged)
+Changes since 2.5.4:
+
+ ENHANCEMENTS:
+
+ * With --progress, when a transfer is complete show the time taken;
+ otherwise show expected time to complete. (Cameron Simpson)
+
+ * Make "make install-strip" works properly, and "make install"
+ accepts a DESTDIR variable for help in building binary packages.
+ (Peter Breitenlohner, Greg Louis)
+
+ * If configured with --enable-maintainer-mode, then on receipt of
+ a fatal signal rsync will try to open an xterm running gdb,
+ similarly to Samba's "panic action" or GNOME's bug-buddy.
+ (Martin Pool)
+
+
+ BUG FIXES:
+
+ * Fix situation where failure to fork (e.g. because out of process
+ slots) would cause rsync to kill all processes owned by the
+ current user. Yes, really! (Paul Haas, Martin Pool)
+
+ * Fix test suite on Solaris. (Jos Backus, Martin Pool)
+
+ * Fix minor memory leak in socket code. (Dave Dykstra, Martin
+ Pool.)
+
+ * Fix --whole-file problem that caused it to be the default even
+ for remote connections. (Martin Pool, Frank Schulz)
+
+ * Work around bug in Mac OS X mkdir(2), which cannot handle
+ trailing slashes.
+
+ (Martin Pool)
+
+ * Improved network error handling. (Greg A. Woods)
+
+
+NEWS for rsync 2.5.4, aka "Imitation lizard skin" (13 Mar 2002)
+Protocol: 26 (unchanged)
+Changes since 2.5.3:
+
+ BUG FIXES:
+
+ * Additional fix for zlib double-free bug. (Martin Pool, Andrew
+ Tridgell) (CVE CAN-2002-0059)
+
+ ENHANCEMENTS:
+
+ * Merge in changes from zlib 1.1.3 to zlib 1.1.4. (Jos Backus)
+ (Note that rsync still uses a custom version of zlib; you can
+ not just link against a system library. See zlib/README.rsync)
+
+ * Additional test cases for --compress. (Martin Pool)
+
+
+NEWS for rsync 2.5.3, aka "Happy 26" (11 Mar 2002)
+Protocol: 26 (unchanged)
+Changes since 2.5.2:
+
+ SECURITY FIXES:
+
+ * Make sure that supplementary groups are removed from a server
+ process after changing uid and gid. (Ethan Benson) (Debian bug
+ #132272, CVE CAN-2002-0080)
+
+ BUG FIXES:
+
+ * Fix zlib double-free bug. (Owen Taylor, Mark J Cox) (CVE
+ CAN-2002-0059)
+
+ * Fixed problem that in many cases caused the error message
+ unexpected read size of 0 in map_ptr
+ and resulted in the wrong data being copied.
+
+ * Fixed compilation errors on some systems caused by the use of
+ "unsigned int64" in rsync.h.
+
+ * Fixed problem on systems such as Sunos4 that do not support realloc
+ on a NULL pointer; error was "out of memory in flist_expand".
+
+ * Fix for rsync server processes hanging around after the client
+ unexpectedly disconnects. (Colin Walters) (Debian bug #128632)
+
+ * Cope with BSD systems on which mkdir() will not accept a trailing
+ slash.
+
+ ENHANCEMENTS:
+
+ * Merge in changes from zlib 1.1.2 to zlib 1.1.3. (Note that
+ rsync still uses a custom version of zlib; you can not just link
+ against a system library. See zlib/README.rsync)
+
+ * Command to initiate connections is only shown with -vv, rather
+ than -v as in 2.5.2. Output from plain -v is more similar to
+ what was historically used so as not to break scripts that try
+ to parse the output.
+
+ * Added --no-whole-file and --no-blocking-io options (Dave Dykstra)
+
+ * Made the --write-batch and --read-batch options actually work
+ and added documentation in the man page (Jos Backus)
+
+ * If the daemon is unable to fork a child to accept a connection,
+ print an error message. (Colin Walters)
+
+
+NEWS for rsync 2.5.2 (26 Jan 2002)
+Protocol: 26 (changed)
+Changes since 2.5.1:
+
+ SECURITY FIXES:
+
+ * Signedness security patch from Sebastian Krahmer
+ -- in some cases we were not sufficiently
+ careful about reading integers from the network.
+
+ BUG FIXES:
+
+ * Fix possible string mangling in log files.
+
+ * Fix for setting local address of outgoing sockets.
+
+ * Better handling of hardlinks and devices on platforms with
+ 64-bit dev_t or ino_t.
+
+ * Name resolution on machines supporting IPv6 is improved.
+
+ * Fix for device nodes. (dann frazier) (Debian #129135)
+
+ ENHANCEMENTS:
+
+ * With -v, rsync now shows the command used to initiate an ssh/rsh
+ connection.
+
+ * --statistics now shows memory heap usage on platforms that
+ support mallinfo().
+
+ * "The Ted T'so school of program optimization": make progress
+ visible and people will think it's faster. (With --progress,
+ rsync will show you how many files it has seen as it builds the
+ file_list, giving some indication that it has not hung.)
+
+ * Improvements to batch mode support. This is still experimental
+ but testing would be welcome. (Jos Backus)
+
+ * New --ignore-existing option, patch previously distributed with
+ Vipul's Razor. (Debian #124286)
+
+
+NEWS for rsync 2.5.1 (3 Jan 2002)
+Protocol: 25 (unchanged)
+Changes since 2.5.0:
+
+ BUG FIXES:
+
+ * Fix for segfault in --daemon mode configuration parser. (Paul
+ Mackerras)
+
+ * Correct string<->address parsing for both IPv4 and 6.
+ (YOSHIFUJI Hideaki, SUMIKAWA Munechika and Jun-ichiro "itojun"
+ Hagino)
+
+ * Various fixes for IPv6 support. (Dave Dykstra)
+
+ * rsync.1 typo fix. (Matt Kraai)
+
+ * Test suite typo fixes. (Tom Schmidt)
+
+ * rsync.1 grammar and clarity improvements. (Edward
+ Welbourne)
+
+ * Correction to ./configure tests for inet_ntop. (Jeff Garzik)
+
+ ENHANCEMENTS:
+
+ * --progress and -P now show estimated data transfer rate (in a
+ multiple of bytes/s) and estimated time to completion. (Rik
+ Faith)
+
+ * --no-detach option, required to run as a W32 service and also
+ useful when running on Unix under daemontools, AIX's SRC, or a
+ debugger. (Max Bowsher, Jos Backus)
+
+ * Clearer error messages for some conditions.
+
+
+NEWS for rsync 2.5.0 (30 Nov 2001)
+Protocol: 25 (changed)
+Changes since 2.4.6:
+
+ ANNOUNCEMENTS
+
+ * Martin Pool is now a co-maintainer.
+
+ NEW FEATURES
+
+ * Support for LSB-compliant packaging
+
+ * Shell wildcards are allowed in "auth users" lines.
+
+ * Merged UNC rsync+ patch to support creation of standalone patch
+ sets. By Bert J. Dempsey and Debra Weiss, updated by Jos
+ Backus.
+
+ * IPv6 support based on a patch from KAME.net, on systems
+ including modern versions of Linux, Solaris, and HP-UX. Also
+ includes IPv6 compatibility functions for old OSs by the
+ Internet Software Consortium, Paul Vixie, the OpenSSH
+ portability project, and OpenBSD.
+
+ ENHANCEMENTS
+
+ * Include/exclude cluestick: with -vv, print out whether files are
+ included or excluded and why.
+
+ * Many error messages have more friendly explanations and more
+ details.
+
+ * Manual page improvements plus scanty protocol documentation.
+
+ * When running as --daemon in the background and using a "log
+ file" rsyncd.conf directive, close the log file every time it is
+ open when going to sleep on the socket. This allows the log
+ file to get cleaned out by another process.
+
+ * Change to using libpopt rather than getopt for processing
+ options. This makes the code cleaner and the behaviour more
+ consistent across platforms. popt is included and built if not
+ installed on the platform.
+
+ * More details in --version, including note about whether 64-bit
+ files, symlinks and hardlinks are supported.
+
+ * MD4 code may use less CPU cycles.
+
+ * Use mkstemp on systems where it is secure. If we use mktemp,
+ explain that we do it in a secure way.
+
+ * --whole-file is the default when source and target are on the
+ local machine.
+
+ BUG FIXES:
+
+ * Fix for various bugs causing rsync to hang.
+
+ * Attempt to fix Large File Summit support on AIX.
+
+ * Attempt to fix error handling lockup bug.
+
+ * Give a non-0 exit code if *any* of the files we have been asked
+ to transfer fail to transfer.
+
+ * For log messages containing ridiculously long strings that might
+ overflow a buffer rsync no longer aborts, but rather prints an
+ ellipsis at the end of the string. (Patch from Ed Santiago.)
+
+ PLATFORMS:
+
+ * Improved support for UNICOS (tested on Cray T3E and Cray SV1)
+
+ * autoconf2.52 (or later) is now required to rebuild the autoconf
+ scripts. It is not required to simply build rsync.
+
+ * Platforms thought to work in this release:
+
+ Cray SV1 UNICOS 10.0.0.8 cc
+ Debian Linux 2.2 UltraSparc gcc
+ Debian Linux testing/unstable ARM gcc
+ FreeBSD 3.3-RELEASE i386 cc
+ FreeBSD 4.1.1-RELEASE i386 cc
+ FreeBSD 4.3-STABLE i386 cc
+ HP PA-RISC HP-UX 10.20 gcc
+ HP PA-RISC HP-UX 11.11 cc
+ IRIX 6.5 MIPS cc
+ IRIX 6.5 MIPS gcc
+ Mac OS X PPC (--disable-ipv6) cc
+ NetBSD 1.5 i386 gcc
+ NetBSD Current i386 cc
+ OpenBSD 2.5 Sparc gcc
+ OpenBSD 2.9 i386 cc
+ OpenBSD Current i386 cc
+ RedHat 6.2 i386 gcc
+ RedHat 6.2 i386 insure++
+ RedHat 7.0 i386 gcc
+ RedHat 7.1 i386 (Kernel 2.4.10) gcc
+ Slackware 8.0 i686 (Kernel 2.4.10)
+ Solaris 8 UltraSparc cc
+ Solaris 8 UltraSparc gcc
+ Solaris 8 i386 gcc
+ SuSE 7.1 i386 gcc2.95.2
+ SuSE 7.1 ppc gcc2.95.2
+ i386-pc-sco3.2v5.0.5 cc
+ i386-pc-sco3.2v5.0.5 gcc
+ powerpc-ibm-aix4.3.3.0 cc
+ i686-unknown-sysv5UnixWare7.1.0 gcc
+ i686-unknown-sysv5UnixWare7.1.0 cc
+
+ TESTING:
+
+ * The existing test.sh script by Phil Hands has been merged into a
+ test framework that works from both "make check" and the Samba
+ build farm.
+
+Partial Protocol History
+ RELEASE DATE VER. DATE OF COMMIT* PROTOCOL
+ 22 Jun 2014 3.1.1 31
+ 28 Sep 2013 3.1.0 31 Aug 2008 31
+ 23 Sep 2011 3.0.9 30
+ 26 Mar 2011 3.0.8 30
+ 31 Dec 2009 3.0.7 30
+ 08 May 2009 3.0.6 30
+ 28 Dec 2008 3.0.5 30
+ 06 Sep 2008 3.0.4 30
+ 29 Jun 2008 3.0.3 30
+ 08 Apr 2008 3.0.2 30
+ 03 Apr 2008 3.0.1 30
+ 01 Mar 2008 3.0.0 11 Nov 2006 30
+ 06 Nov 2006 2.6.9 29
+ 22 Apr 2006 2.6.8 29
+ 11 Mar 2006 2.6.7 29
+ 28 Jul 2005 2.6.6 29
+ 01 Jun 2005 2.6.5 29
+ 30 Mar 2005 2.6.4 17 Jan 2005 29
+ 30 Sep 2004 2.6.3 28
+ 30 Apr 2004 2.6.2 28
+ 26 Apr 2004 2.6.1 08 Jan 2004 28
+ 01 Jan 2004 2.6.0 10 Apr 2003 27 (MAX=40)
+ 04 Dec 2003 2.5.7 26
+ 26 Jan 2003 2.5.6 26
+ 02 Apr 2002 2.5.5 26
+ 13 Mar 2002 2.5.4 26
+ 11 Mar 2002 2.5.3 26
+ 26 Jan 2002 2.5.2 11 Jan 2002 26
+ 03 Jan 2002 2.5.1 25
+ 30 Nov 2001 2.5.0 23 Aug 2001 25
+ 06 Sep 2000 2.4.6 24
+ 19 Aug 2000 2.4.5 24
+ 29 Jul 2000 2.4.4 24
+ 09 Apr 2000 2.4.3 24
+ 30 Mar 2000 2.4.2 24
+ 30 Jan 2000 2.4.1 29 Jan 2000 24
+ 29 Jan 2000 2.4.0 28 Jan 2000 23
+ 25 Jan 2000 2.3.3 23 Jan 2000 22
+ 08 Nov 1999 2.3.2 26 Jun 1999 21
+ 06 Apr 1999 2.3.1 20
+ 15 Mar 1999 2.3.0 15 Mar 1999 20
+ 25 Nov 1998 2.2.1 19
+ 03 Nov 1998 2.2.0 19
+ 09 Sep 1998 2.1.1 19
+ 20 Jul 1998 2.1.0 19
+ 17 Jul 1998 2.0.19 19
+ 18 Jun 1998 2.0.17 19
+ 01 Jun 1998 2.0.16 19
+ 27 May 1998 2.0.13 27 May 1998 19
+ 26 May 1998 2.0.12 18
+ 22 May 1998 2.0.11 18
+ 18 May 1998 2.0.9 18 May 1998 18
+ 17 May 1998 2.0.8 17
+ 15 May 1998 2.0.1 17
+ 14 May 1998 2.0.0 17
+ 17 Apr 1998 1.7.4 17
+ 13 Apr 1998 1.7.3 17
+ 05 Apr 1998 1.7.2 17
+ 26 Mar 1998 1.7.1 17
+ 26 Mar 1998 1.7.0 26 Mar 1998 17 (MAX=30)
+ 13 Jan 1998 1.6.9 13 Jan 1998 15 (MAX=20)
+
+* DATE OF COMMIT is the date the protocol change was committed to CVS.
diff --git a/rsync/README b/rsync/README
new file mode 100644
index 0000000..9007a25
--- /dev/null
+++ b/rsync/README
@@ -0,0 +1,140 @@
+WHAT IS RSYNC?
+--------------
+
+Rsync is a fast and extraordinarily versatile file copying tool for
+both remote and local files.
+
+Rsync uses a delta-transfer algorithm which provides a very fast method
+for bringing remote files into sync. It does this by sending just the
+differences in the files across the link, without requiring that both
+sets of files are present at one of the ends of the link beforehand. At
+first glance this may seem impossible because the calculation of diffs
+between two files normally requires local access to both files.
+
+A technical report describing the rsync algorithm is included with this
+package.
+
+
+USAGE
+-----
+
+Basically you use rsync just like scp, but rsync has many additional
+options. To get a complete list of supported options type:
+
+ rsync --help
+
+See the manpage for more detailed information.
+
+
+SETUP
+-----
+
+Rsync normally uses ssh or rsh for communication with remote systems.
+It does not need to be setuid and requires no special privileges for
+installation. You must, however, have a working ssh or rsh system.
+Using ssh is recommended for its security features.
+
+Alternatively, rsync can run in `daemon' mode, listening on a socket.
+This is generally used for public file distribution, although
+authentication and access control are available.
+
+To install rsync, first run the "configure" script. This will create a
+Makefile and config.h appropriate for your system. Then type "make".
+
+Note that on some systems you will have to force configure not to use
+gcc because gcc may not support some features (such as 64 bit file
+offsets) that your system may support. Set the environment variable CC
+to the name of your native compiler before running configure in this
+case.
+
+Once built put a copy of rsync in your search path on the local and
+remote systems (or use "make install"). That's it!
+
+
+RSYNC DAEMONS
+-------------
+
+Rsync can also talk to "rsync daemons" which can provide anonymous or
+authenticated rsync. See the rsyncd.conf(5) man page for details on how
+to setup an rsync daemon. See the rsync(1) man page for info on how to
+connect to an rsync daemon.
+
+
+WEB SITE
+--------
+
+The main rsync web site is here:
+
+ http://rsync.samba.org/
+
+You'll find a FAQ list, downloads, resources, HTML versions of the
+manpages, etc.
+
+
+MAILING LISTS
+-------------
+
+There is a mailing list for the discussion of rsync and its applications
+that is open to anyone to join. New releases are announced on this
+list, and there is also an announcement-only mailing list for those that
+want official announcements. See the mailing-list page for full
+details:
+
+ http://rsync.samba.org/lists.html
+
+
+BUG REPORTS
+-----------
+
+To visit this web page for full the details on bug reporting:
+
+ http://rsync.samba.org/bugzilla.html
+
+That page contains links to the current bug list, and information on how
+to report a bug well. You might also like to try searching the Internet
+for the error message you've received, or looking in the mailing list
+archives at:
+
+ http://mail-archive.com/rsync@lists.samba.org/
+
+To send a bug report, follow the instructions on the bug-tracking
+page of the web site.
+
+Alternately, email your bug report to rsync@lists.samba.org .
+
+
+GIT REPOSITORY
+--------------
+
+If you want to get the very latest version of rsync direct from the
+source code repository then you can use git:
+
+ git clone git://git.samba.org/rsync.git
+
+See the download page for full details on all the ways to grab the
+source, including nightly tar files, web-browsing of the git repository,
+etc.:
+
+ http://rsync.samba.org/download.html
+
+
+COPYRIGHT
+---------
+
+Rsync was originally written by Andrew Tridgell and is currently
+maintained by Wayne Davison. It has been improved by many developers
+from around the world.
+
+Rsync may be used, modified and redistributed only under the terms of
+the GNU General Public License, found in the file COPYING in this
+distribution, or at:
+
+ http://www.fsf.org/licenses/gpl.html
+
+
+AVAILABILITY
+------------
+
+The main web site for rsync is http://rsync.samba.org/
+The main ftp site is ftp://rsync.samba.org/pub/rsync/
+This is also available as rsync://rsync.samba.org/rsyncftp/
diff --git a/rsync/TODO b/rsync/TODO
new file mode 100644
index 0000000..9baf463
--- /dev/null
+++ b/rsync/TODO
@@ -0,0 +1,528 @@
+-*- indented-text -*-
+
+FEATURES ------------------------------------------------------------
+Use chroot only if supported
+Allow supplementary groups in rsyncd.conf 2002/04/09
+Handling IPv6 on old machines
+Other IPv6 stuff
+Add ACL support 2001/12/02
+proxy authentication 2002/01/23
+SOCKS 2002/01/23
+FAT support
+--diff david.e.sewell 2002/03/15
+Add daemon --no-fork option
+Create more granular verbosity 2003/05/15
+
+DOCUMENTATION --------------------------------------------------------
+Keep list of open issues and todos on the web site
+Perhaps redo manual as SGML
+
+LOGGING --------------------------------------------------------------
+Memory accounting
+Improve error messages
+Better statistics Rasmus 2002/03/08
+Perhaps flush stdout like syslog
+Log child death on signal
+verbose output David Stein 2001/12/20
+internationalization
+
+DEVELOPMENT --------------------------------------------------------
+Handling duplicate names
+Use generic zlib 2002/02/25
+TDB 2002/03/12
+Splint 2002/03/12
+
+PERFORMANCE ----------------------------------------------------------
+Traverse just one directory at a time
+Allow skipping MD4 file_sum 2002/04/08
+Accelerate MD4
+
+TESTING --------------------------------------------------------------
+Torture test
+Cross-test versions 2001/08/22
+Test on kernel source
+Test large files
+Create mutator program for testing
+Create configure option to enable dangerous tests
+Create pipe program for testing
+Create test makefile target for some tests
+
+RELATED PROJECTS -----------------------------------------------------
+rsyncsh
+http://rsync.samba.org/rsync-and-debian/
+rsyncable gzip patch
+rsyncsplit as alternative to real integration with gzip?
+reverse rsync over HTTP Range
+
+
+
+FEATURES ------------------------------------------------------------
+
+
+Use chroot only if supported
+
+ If the platform doesn't support it, then don't even try.
+
+ If running as non-root, then don't fail, just give a warning.
+ (There was a thread about this a while ago?)
+
+ http://lists.samba.org/pipermail/rsync/2001-August/thread.html
+ http://lists.samba.org/pipermail/rsync/2001-September/thread.html
+
+ -- --
+
+
+Allow supplementary groups in rsyncd.conf 2002/04/09
+
+ Perhaps allow supplementary groups to be specified in rsyncd.conf;
+ then make the first one the primary gid and all the rest be
+ supplementary gids.
+
+ -- --
+
+
+Handling IPv6 on old machines
+
+ The KAME IPv6 patch is nice in theory but has proved a bit of a
+ nightmare in practice. The basic idea of their patch is that rsync
+ is rewritten to use the new getaddrinfo()/getnameinfo() interface,
+ rather than gethostbyname()/gethostbyaddr() as in rsync 2.4.6.
+ Systems that don't have the new interface are handled by providing
+ our own implementation in lib/, which is selectively linked in.
+
+ The problem with this is that it is really hard to get right on
+ platforms that have a half-working implementation, so redefining
+ these functions clashes with system headers, and leaving them out
+ breaks. This affects at least OSF/1, RedHat 5, and Cobalt, which
+ are moderately improtant.
+
+ Perhaps the simplest solution would be to have two different files
+ implementing the same interface, and choose either the new or the
+ old API. This is probably necessary for systems that e.g. have
+ IPv6, but gethostbyaddr() can't handle it. The Linux manpage claims
+ this is currently the case.
+
+ In fact, our internal sockets interface (things like
+ open_socket_out(), etc) is much narrower than the getaddrinfo()
+ interface, and so probably simpler to get right. In addition, the
+ old code is known to work well on old machines.
+
+ We could drop the rather large lib/getaddrinfo files.
+
+ -- --
+
+
+Other IPv6 stuff
+
+ Implement suggestions from http://www.kame.net/newsletter/19980604/
+ and ftp://ftp.iij.ad.jp/pub/RFC/rfc2553.txt
+
+ If a host has multiple addresses, then listen try to connect to all
+ in order until we get through. (getaddrinfo may return multiple
+ addresses.) This is kind of implemented already.
+
+ Possibly also when starting as a server we may need to listen on
+ multiple passive addresses. This might be a bit harder, because we
+ may need to select on all of them. Hm.
+
+ -- --
+
+
+Add ACL support 2001/12/02
+
+ Transfer ACLs. Need to think of a standard representation.
+ Probably better not to even try to convert between NT and POSIX.
+ Possibly can share some code with Samba.
+ NOTE: there is a patch that implements this in the "patches" subdir.
+
+ -- --
+
+
+proxy authentication 2002/01/23
+
+ Allow RSYNC_PROXY to be http://user:pass@proxy.foo:3128/, and do
+ HTTP Basic Proxy-Authentication.
+
+ Multiple schemes are possible, up to and including the insanity that
+ is NTLM, but Basic probably covers most cases.
+
+ -- --
+
+
+SOCKS 2002/01/23
+
+ Add --with-socks, and then perhaps a command-line option to put them
+ on or off. This might be more reliable than LD_PRELOAD hacks.
+
+ -- --
+
+
+FAT support
+
+ rsync to a FAT partition on a Unix machine doesn't work very well at
+ the moment. I think we get errors about invalid filenames and
+ perhaps also trying to do atomic renames.
+
+ I guess the code to do this is currently #ifdef'd on Windows;
+ perhaps we ought to intelligently fall back to it on Unix too.
+
+ -- --
+
+
+--diff david.e.sewell 2002/03/15
+
+ Allow people to specify the diff command. (Might want to use wdiff,
+ gnudiff, etc.)
+
+ Just diff the temporary file with the destination file, and delete
+ the tmp file rather than moving it into place.
+
+ Interaction with --partial.
+
+ Security interactions with daemon mode?
+
+ -- --
+
+
+Add daemon --no-fork option
+
+ Very useful for debugging. Also good when running under a
+ daemon-monitoring process that tries to restart the service when the
+ parent exits.
+
+ -- --
+
+
+Create more granular verbosity 2003/05/15
+
+ Control output with the --report option.
+
+ The option takes as a single argument (no whitespace) a
+ comma delimited lists of keywords.
+
+ This would separate debugging from "logging" as well as
+ fine grained selection of statistical reporting and what
+ actions are logged.
+
+ http://lists.samba.org/archive/rsync/2003-May/006059.html
+
+ -- --
+
+DOCUMENTATION --------------------------------------------------------
+
+
+Keep list of open issues and todos on the web site
+
+ -- --
+
+
+Perhaps redo manual as SGML
+
+ The man page is getting rather large, and there is more information
+ that ought to be added.
+
+ TexInfo source is probably a dying format.
+
+ Linuxdoc looks like the most likely contender. I know DocBook is
+ favoured by some people, but it's so bloody verbose, even with emacs
+ support.
+
+ -- --
+
+LOGGING --------------------------------------------------------------
+
+
+Memory accounting
+
+ At exit, show how much memory was used for the file list, etc.
+
+ Also we do a wierd exponential-growth allocation in flist.c. I'm
+ not sure this makes sense with modern mallocs. At any rate it will
+ make us allocate a huge amount of memory for large file lists.
+
+ -- --
+
+
+Improve error messages
+
+ If we hang or get SIGINT, then explain where we were up to. Perhaps
+ have a static buffer that contains the current function name, or
+ some kind of description of what we were trying to do. This is a
+ little easier on people than needing to run strace/truss.
+
+ "The dungeon collapses! You are killed." Rather than "unexpected
+ eof" give a message that is more detailed if possible and also more
+ helpful.
+
+ If we get an error writing to a socket, then we should perhaps
+ continue trying to read to see if an error message comes across
+ explaining why the socket is closed. I'm not sure if this would
+ work, but it would certainly make our messages more helpful.
+
+ What happens if a directory is missing -x attributes. Do we lose
+ our load? (Debian #28416) Probably fixed now, but a test case would
+ be good.
+
+ -- --
+
+
+Better statistics Rasmus 2002/03/08
+
+
+ hey, how about an rsync option that just gives you the
+ summary without the list of files? And perhaps gives
+ more information like the number of new files, number
+ of changed, deleted, etc. ?
+
+
+ nice idea there is --stats but at the moment it's very
+ tridge-oriented rather than user-friendly it would be
+ nice to improve it that would also work well with
+ --dryrun
+
+ -- --
+
+
+Perhaps flush stdout like syslog
+
+ Perhaps flush stdout after each filename, so that people trying to
+ monitor progress in a log file can do so more easily. See
+ http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=48108
+
+ -- --
+
+
+Log child death on signal
+
+ If a child of the rsync daemon dies with a signal, we should notice
+ that when we reap it and log a message.
+
+ -- --
+
+
+verbose output David Stein 2001/12/20
+
+ At end of transfer, show how many files were or were not transferred
+ correctly.
+
+ -- --
+
+
+internationalization
+
+ Change to using gettext(). Probably need to ship this for platforms
+ that don't have it.
+
+ Solicit translations.
+
+ Does anyone care? Before we bother modifying the code, we ought to
+ get the manual translated first, because that's possibly more useful
+ and at any rate demonstrates desire.
+
+ -- --
+
+DEVELOPMENT --------------------------------------------------------
+
+Handling duplicate names
+
+ Some folks would like rsync to be deterministic in how it handles
+ duplicate names that come from mering multiple source directories
+ into a single destination directory; e.g. the last name wins. We
+ could do this by switching our sort algorithm to one that will
+ guarantee that the names won't be reordered. Alternately, we could
+ assign an ever-increasing number to each item as we insert it into
+ the list and then make sure that we leave the largest number when
+ cleaning the file list (see clean_flist()). Another solution would
+ be to add a hash table, and thus never put any duplicate names into
+ the file list (and bump the protocol to handle this).
+
+ -- --
+
+
+Use generic zlib 2002/02/25
+
+ Perhaps don't use our own zlib.
+
+ Advantages:
+
+ - will automatically be up to date with bugfixes in zlib
+
+ - can leave it out for small rsync on e.g. recovery disks
+
+ - can use a shared library
+
+ - avoids people breaking rsync by trying to do this themselves and
+ messing up
+
+ Should we ship zlib for systems that don't have it, or require
+ people to install it separately?
+
+ Apparently this will make us incompatible with versions of rsync
+ that use the patched version of rsync. Probably the simplest way to
+ do this is to just disable gzip (with a warning) when talking to old
+ versions.
+
+ -- --
+
+
+Splint 2002/03/12
+
+ Build rsync with SPLINT to try to find security holes. Add
+ annotations as necessary. Keep track of the number of warnings
+ found initially, and see how many of them are real bugs, or real
+ security bugs. Knowing the percentage of likely hits would be
+ really interesting for other projects.
+
+ -- --
+
+PERFORMANCE ----------------------------------------------------------
+
+Allow skipping MD4 file_sum 2002/04/08
+
+ If we're doing a local transfer, or using -W, then perhaps don't
+ send the file checksum. If we're doing a local transfer, then
+ calculating MD4 checksums uses 90% of CPU and is unlikely to be
+ useful.
+
+ We should not allow it to be disabled separately from -W, though
+ as it is the only thing that lets us know when the rsync algorithm
+ got out of sync and messed the file up (i.e. if the basis file
+ changed between checksum generation and reception).
+
+ -- --
+
+
+Accelerate MD4
+
+ Perhaps borrow an assembler MD4 from someone?
+
+ Make sure we call MD4 with properly-sized blocks whenever possible
+ to avoid copying into the residue region?
+
+ -- --
+
+TESTING --------------------------------------------------------------
+
+Torture test
+
+ Something that just keeps running rsync continuously over a data set
+ likely to generate problems.
+
+ -- --
+
+
+Cross-test versions 2001/08/22
+
+ Part of the regression suite should be making sure that we
+ don't break backwards compatibility: old clients vs new
+ servers and so on. Ideally we would test both up and down
+ from the current release to all old versions.
+
+ Run current rsync versions against significant past releases.
+
+ We might need to omit broken old versions, or versions in which
+ particular functionality is broken
+
+ It might be sufficient to test downloads from well-known public
+ rsync servers running different versions of rsync. This will give
+ some testing and also be the most common case for having different
+ versions and not being able to upgrade.
+
+ The new --protocol option may help in this.
+
+ -- --
+
+
+Test on kernel source
+
+ Download all versions of kernel; unpack, sync between them. Also
+ sync between uncompressed tarballs. Compare directories after
+ transfer.
+
+ Use local mode; ssh; daemon; --whole-file and --no-whole-file.
+
+ Use awk to pull out the 'speedup' number for each transfer. Make
+ sure it is >= x.
+
+ -- --
+
+
+Test large files
+
+ Sparse and non-sparse
+
+ -- --
+
+
+Create mutator program for testing
+
+ Insert bytes, delete bytes, swap blocks, ...
+
+ -- --
+
+
+Create configure option to enable dangerous tests
+
+ -- --
+
+
+Create pipe program for testing
+
+ Create pipe program that makes slow/jerky connections for
+ testing Versions of read() and write() that corrupt the
+ stream, or abruptly fail
+
+ -- --
+
+
+Create test makefile target for some tests
+
+ Separate makefile target to run rough tests -- or perhaps
+ just run them every time?
+
+ -- --
+
+RELATED PROJECTS -----------------------------------------------------
+
+rsyncsh
+
+ Write a small emulation of interactive ftp as a Pythonn program
+ that calls rsync. Commands such as "cd", "ls", "ls *.c" etc map
+ fairly directly into rsync commands: it just needs to remember the
+ current host, directory and so on. We can probably even do
+ completion of remote filenames.
+
+ -- --
+
+
+http://rsync.samba.org/rsync-and-debian/
+
+
+ -- --
+
+
+rsyncable gzip patch
+
+ Exhaustive, tortuous testing
+
+ Cleanups?
+
+ -- --
+
+
+rsyncsplit as alternative to real integration with gzip?
+
+ -- --
+
+
+reverse rsync over HTTP Range
+
+ Goswin Brederlow suggested this on Debian; I think tridge and I
+ talked about it previous in relation to rproxy.
+
+ Addendum: It looks like someone is working on a version of this:
+
+ http://zsync.moria.org.uk/
+
+ -- --
+
diff --git a/rsync/access.c b/rsync/access.c
new file mode 100644
index 0000000..df89d6d
--- /dev/null
+++ b/rsync/access.c
@@ -0,0 +1,290 @@
+/*
+ * Routines to authenticate access to a daemon (hosts allow/deny).
+ *
+ * Copyright (C) 1998 Andrew Tridgell
+ * Copyright (C) 2004-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+static int allow_forward_dns;
+
+extern const char undetermined_hostname[];
+
+static int match_hostname(const char **host_ptr, const char *addr, const char *tok)
+{
+ struct hostent *hp;
+ unsigned int i;
+ const char *host = *host_ptr;
+
+ if (!host || !*host)
+ return 0;
+
+ /* First check if the reverse-DNS-determined hostname matches. */
+ if (iwildmatch(tok, host))
+ return 1;
+
+ if (!allow_forward_dns)
+ return 0;
+
+ /* Fail quietly if tok is an address or wildcarded entry, not a simple hostname. */
+ if (!tok[strspn(tok, ".0123456789")] || tok[strcspn(tok, ":/*?[")])
+ return 0;
+
+ /* Now try forward-DNS on the token (config-specified hostname) and see if the IP matches. */
+ if (!(hp = gethostbyname(tok)))
+ return 0;
+
+ for (i = 0; hp->h_addr_list[i] != NULL; i++) {
+ if (strcmp(addr, inet_ntoa(*(struct in_addr*)(hp->h_addr_list[i]))) == 0) {
+ /* If reverse lookups are off, we'll use the conf-specified
+ * hostname in preference to UNDETERMINED. */
+ if (host == undetermined_hostname) {
+ if (!(*host_ptr = strdup(tok)))
+ *host_ptr = undetermined_hostname;
+ }
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int match_binary(const char *b1, const char *b2, const char *mask, int addrlen)
+{
+ int i;
+
+ for (i = 0; i < addrlen; i++) {
+ if ((b1[i] ^ b2[i]) & mask[i])
+ return 0;
+ }
+
+ return 1;
+}
+
+static void make_mask(char *mask, int plen, int addrlen)
+{
+ int w, b;
+
+ w = plen >> 3;
+ b = plen & 0x7;
+
+ if (w)
+ memset(mask, 0xff, w);
+ if (w < addrlen)
+ mask[w] = 0xff & (0xff<<(8-b));
+ if (w+1 < addrlen)
+ memset(mask+w+1, 0, addrlen-w-1);
+
+ return;
+}
+
+static int match_address(const char *addr, const char *tok)
+{
+ char *p;
+ struct addrinfo hints, *resa, *rest;
+ int gai;
+ int ret = 0;
+ int addrlen = 0;
+#ifdef HAVE_STRTOL
+ long int bits;
+#else
+ int bits;
+#endif
+ char mask[16];
+ char *a = NULL, *t = NULL;
+
+ if (!addr || !*addr)
+ return 0;
+
+ p = strchr(tok,'/');
+ if (p)
+ *p = '\0';
+
+ /* Fail quietly if tok is a hostname, not an address. */
+ if (tok[strspn(tok, ".0123456789")] && strchr(tok, ':') == NULL) {
+ if (p)
+ *p = '/';
+ return 0;
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+#ifdef AI_NUMERICHOST
+ hints.ai_flags = AI_NUMERICHOST;
+#endif
+
+ if (getaddrinfo(addr, NULL, &hints, &resa) != 0) {
+ if (p)
+ *p = '/';
+ return 0;
+ }
+
+ gai = getaddrinfo(tok, NULL, &hints, &rest);
+ if (p)
+ *p++ = '/';
+ if (gai != 0) {
+ rprintf(FLOG, "error matching address %s: %s\n",
+ tok, gai_strerror(gai));
+ freeaddrinfo(resa);
+ return 0;
+ }
+
+ if (rest->ai_family != resa->ai_family) {
+ ret = 0;
+ goto out;
+ }
+
+ switch(resa->ai_family) {
+ case PF_INET:
+ a = (char *)&((struct sockaddr_in *)resa->ai_addr)->sin_addr;
+ t = (char *)&((struct sockaddr_in *)rest->ai_addr)->sin_addr;
+ addrlen = 4;
+
+ break;
+
+#ifdef INET6
+ case PF_INET6:
+ {
+ struct sockaddr_in6 *sin6a, *sin6t;
+
+ sin6a = (struct sockaddr_in6 *)resa->ai_addr;
+ sin6t = (struct sockaddr_in6 *)rest->ai_addr;
+
+ a = (char *)&sin6a->sin6_addr;
+ t = (char *)&sin6t->sin6_addr;
+
+ addrlen = 16;
+
+#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
+ if (sin6t->sin6_scope_id &&
+ sin6a->sin6_scope_id != sin6t->sin6_scope_id) {
+ ret = 0;
+ goto out;
+ }
+#endif
+
+ break;
+ }
+#endif
+ default:
+ rprintf(FLOG, "unknown family %u\n", rest->ai_family);
+ ret = 0;
+ goto out;
+ }
+
+ bits = -1;
+ if (p) {
+ if (inet_pton(resa->ai_addr->sa_family, p, mask) <= 0) {
+#ifdef HAVE_STRTOL
+ char *ep = NULL;
+#else
+ unsigned char *pp;
+#endif
+
+#ifdef HAVE_STRTOL
+ bits = strtol(p, &ep, 10);
+ if (!*p || *ep) {
+ rprintf(FLOG, "malformed mask in %s\n", tok);
+ ret = 0;
+ goto out;
+ }
+#else
+ for (pp = (unsigned char *)p; *pp; pp++) {
+ if (!isascii(*pp) || !isdigit(*pp)) {
+ rprintf(FLOG, "malformed mask in %s\n", tok);
+ ret = 0;
+ goto out;
+ }
+ }
+ bits = atoi(p);
+#endif
+ if (bits == 0) {
+ ret = 1;
+ goto out;
+ }
+ if (bits < 0 || bits > (addrlen << 3)) {
+ rprintf(FLOG, "malformed mask in %s\n", tok);
+ ret = 0;
+ goto out;
+ }
+ }
+ } else {
+ bits = 128;
+ }
+
+ if (bits >= 0)
+ make_mask(mask, bits, addrlen);
+
+ ret = match_binary(a, t, mask, addrlen);
+
+ out:
+ freeaddrinfo(resa);
+ freeaddrinfo(rest);
+ return ret;
+}
+
+static int access_match(const char *list, const char *addr, const char **host_ptr)
+{
+ char *tok;
+ char *list2 = strdup(list);
+
+ if (!list2)
+ out_of_memory("access_match");
+
+ strlower(list2);
+
+ for (tok = strtok(list2, " ,\t"); tok; tok = strtok(NULL, " ,\t")) {
+ if (match_hostname(host_ptr, addr, tok) || match_address(addr, tok)) {
+ free(list2);
+ return 1;
+ }
+ }
+
+ free(list2);
+ return 0;
+}
+
+int allow_access(const char *addr, const char **host_ptr, int i)
+{
+ const char *allow_list = lp_hosts_allow(i);
+ const char *deny_list = lp_hosts_deny(i);
+
+ if (allow_list && !*allow_list)
+ allow_list = NULL;
+ if (deny_list && !*deny_list)
+ deny_list = NULL;
+
+ allow_forward_dns = lp_forward_lookup(i);
+
+ /* If we match an allow-list item, we always allow access. */
+ if (allow_list) {
+ if (access_match(allow_list, addr, host_ptr))
+ return 1;
+ /* For an allow-list w/o a deny-list, disallow non-matches. */
+ if (!deny_list)
+ return 0;
+ }
+
+ /* If we match a deny-list item (and got past any allow-list
+ * items), we always disallow access. */
+ if (deny_list && access_match(deny_list, addr, host_ptr))
+ return 0;
+
+ /* Allow all other access. */
+ return 1;
+}
diff --git a/rsync/aclocal.m4 b/rsync/aclocal.m4
new file mode 100644
index 0000000..d489b20
--- /dev/null
+++ b/rsync/aclocal.m4
@@ -0,0 +1,92 @@
+dnl AC_VALIDATE_CACHE_SYSTEM_TYPE[(cmd)]
+dnl if the cache file is inconsistent with the current host,
+dnl target and build system types, execute CMD or print a default
+dnl error message.
+AC_DEFUN(AC_VALIDATE_CACHE_SYSTEM_TYPE, [
+ AC_REQUIRE([AC_CANONICAL_SYSTEM])
+ AC_MSG_CHECKING([config.cache system type])
+ if { test x"${ac_cv_host_system_type+set}" = x"set" &&
+ test x"$ac_cv_host_system_type" != x"$host"; } ||
+ { test x"${ac_cv_build_system_type+set}" = x"set" &&
+ test x"$ac_cv_build_system_type" != x"$build"; } ||
+ { test x"${ac_cv_target_system_type+set}" = x"set" &&
+ test x"$ac_cv_target_system_type" != x"$target"; }; then
+ AC_MSG_RESULT([different])
+ ifelse($#, 1, [$1],
+ [AC_MSG_ERROR(["you must remove config.cache and restart configure"])])
+ else
+ AC_MSG_RESULT([same])
+ fi
+ ac_cv_host_system_type="$host"
+ ac_cv_build_system_type="$build"
+ ac_cv_target_system_type="$target"
+])
+
+dnl Check for socklen_t: historically on BSD it is an int, and in
+dnl POSIX 1g it is a type of its own, but some platforms use different
+dnl types for the argument to getsockopt, getpeername, etc. So we
+dnl have to test to find something that will work.
+
+dnl This is no good, because passing the wrong pointer on C compilers is
+dnl likely to only generate a warning, not an error. We don't call this at
+dnl the moment.
+
+AC_DEFUN([TYPE_SOCKLEN_T],
+[
+ AC_CHECK_TYPE([socklen_t], ,[
+ AC_MSG_CHECKING([for socklen_t equivalent])
+ AC_CACHE_VAL([rsync_cv_socklen_t_equiv],
+ [
+ # Systems have either "struct sockaddr *" or
+ # "void *" as the second argument to getpeername
+ rsync_cv_socklen_t_equiv=
+ for arg2 in "struct sockaddr" void; do
+ for t in int size_t unsigned long "unsigned long"; do
+ AC_TRY_COMPILE([
+#include
+#include
+
+ int getpeername (int, $arg2 *, $t *);
+ ],[
+ $t len;
+ getpeername(0,0,&len);
+ ],[
+ rsync_cv_socklen_t_equiv="$t"
+ break
+ ])
+ done
+ done
+
+ if test "x$rsync_cv_socklen_t_equiv" = x; then
+ AC_MSG_ERROR([Cannot find a type to use in place of socklen_t])
+ fi
+ ])
+ AC_MSG_RESULT($rsync_cv_socklen_t_equiv)
+ AC_DEFINE_UNQUOTED(socklen_t, $rsync_cv_socklen_t_equiv,
+ [type to use in place of socklen_t if not defined])],
+ [#include
+#include ])
+])
+
+dnl AC_HAVE_TYPE(TYPE,INCLUDES)
+AC_DEFUN([AC_HAVE_TYPE], [
+AC_REQUIRE([AC_HEADER_STDC])
+cv=`echo "$1" | sed 'y%./+- %__p__%'`
+AC_MSG_CHECKING(for $1)
+AC_CACHE_VAL([ac_cv_type_$cv],
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+AC_INCLUDES_DEFAULT
+$2]],
+[[$1 foo;]])],
+[eval "ac_cv_type_$cv=yes"],
+[eval "ac_cv_type_$cv=no"]))dnl
+ac_foo=`eval echo \\$ac_cv_type_$cv`
+AC_MSG_RESULT($ac_foo)
+if test "$ac_foo" = yes; then
+ ac_tr_hdr=HAVE_`echo $1 | sed 'y%abcdefghijklmnopqrstuvwxyz./- %ABCDEFGHIJKLMNOPQRSTUVWXYZ____%'`
+if false; then
+ AC_CHECK_TYPES($1)
+fi
+ AC_DEFINE_UNQUOTED($ac_tr_hdr, 1, [Define if you have type `$1'])
+fi
+])
diff --git a/rsync/acls.c b/rsync/acls.c
new file mode 100644
index 0000000..ec1afc4
--- /dev/null
+++ b/rsync/acls.c
@@ -0,0 +1,1155 @@
+/*
+ * Handle passing Access Control Lists between systems.
+ *
+ * Copyright (C) 1996 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2006-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "lib/sysacls.h"
+
+#ifdef SUPPORT_ACLS
+
+extern int dry_run;
+extern int am_root;
+extern int read_only;
+extern int list_only;
+extern int orig_umask;
+extern int numeric_ids;
+extern int inc_recurse;
+extern int preserve_devices;
+extern int preserve_specials;
+
+/* Flags used to indicate what items are being transmitted for an entry. */
+#define XMIT_USER_OBJ (1<<0)
+#define XMIT_GROUP_OBJ (1<<1)
+#define XMIT_MASK_OBJ (1<<2)
+#define XMIT_OTHER_OBJ (1<<3)
+#define XMIT_NAME_LIST (1<<4)
+
+#define NO_ENTRY ((uchar)0x80) /* Default value of a NON-name-list entry. */
+
+#define NAME_IS_USER (1u<<31) /* Bit used only on a name-list entry. */
+
+/* When we send the access bits over the wire, we shift them 2 bits to the
+ * left and use the lower 2 bits as flags (relevant only to a name entry).
+ * This makes the protocol more efficient than sending a value that would
+ * be likely to have its hightest bits set. */
+#define XFLAG_NAME_FOLLOWS 0x0001u
+#define XFLAG_NAME_IS_USER 0x0002u
+
+/* === ACL structures === */
+
+typedef struct {
+ id_t id;
+ uint32 access;
+} id_access;
+
+typedef struct {
+ id_access *idas;
+ int count;
+} ida_entries;
+
+typedef struct {
+ char *name;
+ uchar len;
+} idname;
+
+typedef struct rsync_acl {
+ ida_entries names;
+ /* These will be NO_ENTRY if there's no such entry. */
+ uchar user_obj;
+ uchar group_obj;
+ uchar mask_obj;
+ uchar other_obj;
+} rsync_acl;
+
+typedef struct {
+ rsync_acl racl;
+ SMB_ACL_T sacl;
+} acl_duo;
+
+static const rsync_acl empty_rsync_acl = {
+ {NULL, 0}, NO_ENTRY, NO_ENTRY, NO_ENTRY, NO_ENTRY
+};
+
+static item_list access_acl_list = EMPTY_ITEM_LIST;
+static item_list default_acl_list = EMPTY_ITEM_LIST;
+
+static size_t prior_access_count = (size_t)-1;
+static size_t prior_default_count = (size_t)-1;
+
+/* === Calculations on ACL types === */
+
+static const char *str_acl_type(SMB_ACL_TYPE_T type)
+{
+ switch (type) {
+ case SMB_ACL_TYPE_ACCESS:
+#ifdef HAVE_OSX_ACLS
+ return "ACL_TYPE_EXTENDED";
+#else
+ return "ACL_TYPE_ACCESS";
+#endif
+ case SMB_ACL_TYPE_DEFAULT:
+ return "ACL_TYPE_DEFAULT";
+ default:
+ break;
+ }
+ return "unknown ACL type!";
+}
+
+static int calc_sacl_entries(const rsync_acl *racl)
+{
+ /* A System ACL always gets user/group/other permission entries. */
+ return racl->names.count
+#ifdef ACLS_NEED_MASK
+ + 1
+#else
+ + (racl->mask_obj != NO_ENTRY)
+#endif
+ + 3;
+}
+
+/* Extracts and returns the permission bits from the ACL. This cannot be
+ * called on an rsync_acl that has NO_ENTRY in any spot but the mask. */
+static int rsync_acl_get_perms(const rsync_acl *racl)
+{
+ return (racl->user_obj << 6)
+ + ((racl->mask_obj != NO_ENTRY ? racl->mask_obj : racl->group_obj) << 3)
+ + racl->other_obj;
+}
+
+/* Removes the permission-bit entries from the ACL because these
+ * can be reconstructed from the file's mode. */
+static void rsync_acl_strip_perms(stat_x *sxp)
+{
+ rsync_acl *racl = sxp->acc_acl;
+
+ racl->user_obj = NO_ENTRY;
+ if (racl->mask_obj == NO_ENTRY)
+ racl->group_obj = NO_ENTRY;
+ else {
+ int group_perms = (sxp->st.st_mode >> 3) & 7;
+ if (racl->group_obj == group_perms)
+ racl->group_obj = NO_ENTRY;
+#ifndef HAVE_SOLARIS_ACLS
+ if (racl->names.count != 0 && racl->mask_obj == group_perms)
+ racl->mask_obj = NO_ENTRY;
+#endif
+ }
+ racl->other_obj = NO_ENTRY;
+}
+
+/* Given an empty rsync_acl, fake up the permission bits. */
+static void rsync_acl_fake_perms(rsync_acl *racl, mode_t mode)
+{
+ racl->user_obj = (mode >> 6) & 7;
+ racl->group_obj = (mode >> 3) & 7;
+ racl->other_obj = mode & 7;
+}
+
+/* === Rsync ACL functions === */
+
+static rsync_acl *create_racl(void)
+{
+ rsync_acl *racl = new(rsync_acl);
+
+ if (!racl)
+ out_of_memory("create_racl");
+ *racl = empty_rsync_acl;
+
+ return racl;
+}
+
+static BOOL ida_entries_equal(const ida_entries *ial1, const ida_entries *ial2)
+{
+ id_access *ida1, *ida2;
+ int count = ial1->count;
+ if (count != ial2->count)
+ return False;
+ ida1 = ial1->idas;
+ ida2 = ial2->idas;
+ for (; count--; ida1++, ida2++) {
+ if (ida1->access != ida2->access || ida1->id != ida2->id)
+ return False;
+ }
+ return True;
+}
+
+static BOOL rsync_acl_equal(const rsync_acl *racl1, const rsync_acl *racl2)
+{
+ return racl1->user_obj == racl2->user_obj
+ && racl1->group_obj == racl2->group_obj
+ && racl1->mask_obj == racl2->mask_obj
+ && racl1->other_obj == racl2->other_obj
+ && ida_entries_equal(&racl1->names, &racl2->names);
+}
+
+/* Are the extended (non-permission-bit) entries equal? If so, the rest of
+ * the ACL will be handled by the normal mode-preservation code. This is
+ * only meaningful for access ACLs! Note: the 1st arg is a fully-populated
+ * rsync_acl, but the 2nd parameter can be a condensed rsync_acl, which means
+ * that it might have several of its permission objects set to NO_ENTRY. */
+static BOOL rsync_acl_equal_enough(const rsync_acl *racl1,
+ const rsync_acl *racl2, mode_t m)
+{
+ if ((racl1->mask_obj ^ racl2->mask_obj) & NO_ENTRY)
+ return False; /* One has a mask and the other doesn't */
+
+ /* When there's a mask, the group_obj becomes an extended entry. */
+ if (racl1->mask_obj != NO_ENTRY) {
+ /* A condensed rsync_acl with a mask can only have no
+ * group_obj when it was identical to the mask. This
+ * means that it was also identical to the group attrs
+ * from the mode. */
+ if (racl2->group_obj == NO_ENTRY) {
+ if (racl1->group_obj != ((m >> 3) & 7))
+ return False;
+ } else if (racl1->group_obj != racl2->group_obj)
+ return False;
+ }
+ return ida_entries_equal(&racl1->names, &racl2->names);
+}
+
+static void rsync_acl_free(rsync_acl *racl)
+{
+ if (racl->names.idas)
+ free(racl->names.idas);
+ *racl = empty_rsync_acl;
+}
+
+void free_acl(stat_x *sxp)
+{
+ if (sxp->acc_acl) {
+ rsync_acl_free(sxp->acc_acl);
+ free(sxp->acc_acl);
+ sxp->acc_acl = NULL;
+ }
+ if (sxp->def_acl) {
+ rsync_acl_free(sxp->def_acl);
+ free(sxp->def_acl);
+ sxp->def_acl = NULL;
+ }
+}
+
+#ifdef SMB_ACL_NEED_SORT
+static int id_access_sorter(const void *r1, const void *r2)
+{
+ id_access *ida1 = (id_access *)r1;
+ id_access *ida2 = (id_access *)r2;
+ id_t rid1 = ida1->id, rid2 = ida2->id;
+ if ((ida1->access ^ ida2->access) & NAME_IS_USER)
+ return ida1->access & NAME_IS_USER ? -1 : 1;
+ return rid1 == rid2 ? 0 : rid1 < rid2 ? -1 : 1;
+}
+#endif
+
+/* === System ACLs === */
+
+/* Unpack system ACL -> rsync ACL verbatim. Return whether we succeeded. */
+static BOOL unpack_smb_acl(SMB_ACL_T sacl, rsync_acl *racl)
+{
+ static item_list temp_ida_list = EMPTY_ITEM_LIST;
+ SMB_ACL_ENTRY_T entry;
+ const char *errfun;
+ int rc;
+
+ errfun = "sys_acl_get_entry";
+ for (rc = sys_acl_get_entry(sacl, SMB_ACL_FIRST_ENTRY, &entry);
+ rc == 1;
+ rc = sys_acl_get_entry(sacl, SMB_ACL_NEXT_ENTRY, &entry)) {
+ SMB_ACL_TAG_T tag_type;
+ uint32 access;
+ id_t g_u_id;
+ id_access *ida;
+ if ((rc = sys_acl_get_info(entry, &tag_type, &access, &g_u_id)) != 0) {
+ errfun = "sys_acl_get_info";
+ break;
+ }
+ /* continue == done with entry; break == store in temporary ida list */
+ switch (tag_type) {
+#ifndef HAVE_OSX_ACLS
+ case SMB_ACL_USER_OBJ:
+ if (racl->user_obj == NO_ENTRY)
+ racl->user_obj = access;
+ else
+ rprintf(FINFO, "unpack_smb_acl: warning: duplicate USER_OBJ entry ignored\n");
+ continue;
+ case SMB_ACL_GROUP_OBJ:
+ if (racl->group_obj == NO_ENTRY)
+ racl->group_obj = access;
+ else
+ rprintf(FINFO, "unpack_smb_acl: warning: duplicate GROUP_OBJ entry ignored\n");
+ continue;
+ case SMB_ACL_MASK:
+ if (racl->mask_obj == NO_ENTRY)
+ racl->mask_obj = access;
+ else
+ rprintf(FINFO, "unpack_smb_acl: warning: duplicate MASK entry ignored\n");
+ continue;
+ case SMB_ACL_OTHER:
+ if (racl->other_obj == NO_ENTRY)
+ racl->other_obj = access;
+ else
+ rprintf(FINFO, "unpack_smb_acl: warning: duplicate OTHER entry ignored\n");
+ continue;
+#endif
+ case SMB_ACL_USER:
+ access |= NAME_IS_USER;
+ break;
+ case SMB_ACL_GROUP:
+ break;
+ default:
+ rprintf(FINFO, "unpack_smb_acl: warning: entry with unrecognized tag type ignored\n");
+ continue;
+ }
+ ida = EXPAND_ITEM_LIST(&temp_ida_list, id_access, -10);
+ ida->id = g_u_id;
+ ida->access = access;
+ }
+ if (rc) {
+ rsyserr(FERROR_XFER, errno, "unpack_smb_acl: %s()", errfun);
+ rsync_acl_free(racl);
+ return False;
+ }
+
+ /* Transfer the count id_access items out of the temp_ida_list
+ * into the names ida_entries list in racl. */
+ if (temp_ida_list.count) {
+#ifdef SMB_ACL_NEED_SORT
+ if (temp_ida_list.count > 1) {
+ qsort(temp_ida_list.items, temp_ida_list.count,
+ sizeof (id_access), id_access_sorter);
+ }
+#endif
+ if (!(racl->names.idas = new_array(id_access, temp_ida_list.count)))
+ out_of_memory("unpack_smb_acl");
+ memcpy(racl->names.idas, temp_ida_list.items,
+ temp_ida_list.count * sizeof (id_access));
+ } else
+ racl->names.idas = NULL;
+
+ racl->names.count = temp_ida_list.count;
+
+ /* Truncate the temporary list now that its idas have been saved. */
+ temp_ida_list.count = 0;
+
+ return True;
+}
+
+/* Synactic sugar for system calls */
+
+#define CALL_OR_ERROR(func,args,str) \
+ do { \
+ if (func args) { \
+ errfun = str; \
+ goto error_exit; \
+ } \
+ } while (0)
+
+#define COE(func,args) CALL_OR_ERROR(func,args,#func)
+#define COE2(func,args) CALL_OR_ERROR(func,args,NULL)
+
+#ifndef HAVE_OSX_ACLS
+/* Store the permissions in the system ACL entry. */
+static int store_access_in_entry(uint32 access, SMB_ACL_ENTRY_T entry)
+{
+ if (sys_acl_set_access_bits(entry, access)) {
+ rsyserr(FERROR_XFER, errno, "store_access_in_entry sys_acl_set_access_bits()");
+ return -1;
+ }
+ return 0;
+}
+#endif
+
+/* Pack rsync ACL -> system ACL verbatim. Return whether we succeeded. */
+static BOOL pack_smb_acl(SMB_ACL_T *smb_acl, const rsync_acl *racl)
+{
+#ifdef ACLS_NEED_MASK
+ uchar mask_bits;
+#endif
+ size_t count;
+ id_access *ida;
+ const char *errfun = NULL;
+ SMB_ACL_ENTRY_T entry;
+
+ if (!(*smb_acl = sys_acl_init(calc_sacl_entries(racl)))) {
+ rsyserr(FERROR_XFER, errno, "pack_smb_acl: sys_acl_init()");
+ return False;
+ }
+
+#ifndef HAVE_OSX_ACLS
+ COE( sys_acl_create_entry,(smb_acl, &entry) );
+ COE( sys_acl_set_info,(entry, SMB_ACL_USER_OBJ, racl->user_obj & ~NO_ENTRY, 0) );
+#endif
+
+ for (ida = racl->names.idas, count = racl->names.count; count; ida++, count--) {
+#ifdef SMB_ACL_NEED_SORT
+ if (!(ida->access & NAME_IS_USER))
+ break;
+#endif
+ COE( sys_acl_create_entry,(smb_acl, &entry) );
+ COE( sys_acl_set_info,
+ (entry,
+ ida->access & NAME_IS_USER ? SMB_ACL_USER : SMB_ACL_GROUP,
+ ida->access & ~NAME_IS_USER, ida->id) );
+ }
+
+#ifndef HAVE_OSX_ACLS
+ COE( sys_acl_create_entry,(smb_acl, &entry) );
+ COE( sys_acl_set_info,(entry, SMB_ACL_GROUP_OBJ, racl->group_obj & ~NO_ENTRY, 0) );
+
+#ifdef SMB_ACL_NEED_SORT
+ for ( ; count; ida++, count--) {
+ COE( sys_acl_create_entry,(smb_acl, &entry) );
+ COE( sys_acl_set_info,(entry, SMB_ACL_GROUP, ida->access, ida->id) );
+ }
+#endif
+
+#ifdef ACLS_NEED_MASK
+ mask_bits = racl->mask_obj == NO_ENTRY ? racl->group_obj & ~NO_ENTRY : racl->mask_obj;
+ COE( sys_acl_create_entry,(smb_acl, &entry) );
+ COE( sys_acl_set_info,(entry, SMB_ACL_MASK, mask_bits, 0) );
+#else
+ if (racl->mask_obj != NO_ENTRY) {
+ COE( sys_acl_create_entry,(smb_acl, &entry) );
+ COE( sys_acl_set_info,(entry, SMB_ACL_MASK, racl->mask_obj, 0) );
+ }
+#endif
+
+ COE( sys_acl_create_entry,(smb_acl, &entry) );
+ COE( sys_acl_set_info,(entry, SMB_ACL_OTHER, racl->other_obj & ~NO_ENTRY, 0) );
+#endif
+
+#ifdef DEBUG
+ if (sys_acl_valid(*smb_acl) < 0)
+ rprintf(FERROR_XFER, "pack_smb_acl: warning: system says the ACL I packed is invalid\n");
+#endif
+
+ return True;
+
+ error_exit:
+ if (errfun) {
+ rsyserr(FERROR_XFER, errno, "pack_smb_acl %s()", errfun);
+ }
+ sys_acl_free_acl(*smb_acl);
+ return False;
+}
+
+static int find_matching_rsync_acl(const rsync_acl *racl, SMB_ACL_TYPE_T type,
+ const item_list *racl_list)
+{
+ static int access_match = -1, default_match = -1;
+ int *match = type == SMB_ACL_TYPE_ACCESS ? &access_match : &default_match;
+ size_t count = racl_list->count;
+
+ /* If this is the first time through or we didn't match the last
+ * time, then start at the end of the list, which should be the
+ * best place to start hunting. */
+ if (*match == -1)
+ *match = racl_list->count - 1;
+ while (count--) {
+ rsync_acl *base = racl_list->items;
+ if (rsync_acl_equal(base + *match, racl))
+ return *match;
+ if (!(*match)--)
+ *match = racl_list->count - 1;
+ }
+
+ *match = -1;
+ return *match;
+}
+
+static int get_rsync_acl(const char *fname, rsync_acl *racl,
+ SMB_ACL_TYPE_T type, mode_t mode)
+{
+ SMB_ACL_T sacl;
+
+#ifdef SUPPORT_XATTRS
+ /* --fake-super support: load ACLs from an xattr. */
+ if (am_root < 0) {
+ char *buf;
+ size_t len;
+ int cnt;
+
+ if ((buf = get_xattr_acl(fname, type == SMB_ACL_TYPE_ACCESS, &len)) == NULL)
+ return 0;
+ cnt = (len - 4*4) / (4+4);
+ if (len < 4*4 || len != (size_t)cnt*(4+4) + 4*4) {
+ free(buf);
+ return -1;
+ }
+
+ racl->user_obj = IVAL(buf, 0);
+ if (racl->user_obj == NO_ENTRY)
+ racl->user_obj = (mode >> 6) & 7;
+ racl->group_obj = IVAL(buf, 4);
+ if (racl->group_obj == NO_ENTRY)
+ racl->group_obj = (mode >> 3) & 7;
+ racl->mask_obj = IVAL(buf, 8);
+ racl->other_obj = IVAL(buf, 12);
+ if (racl->other_obj == NO_ENTRY)
+ racl->other_obj = mode & 7;
+
+ if (cnt) {
+ char *bp = buf + 4*4;
+ id_access *ida;
+ if (!(ida = racl->names.idas = new_array(id_access, cnt)))
+ out_of_memory("get_rsync_acl");
+ racl->names.count = cnt;
+ for ( ; cnt--; ida++, bp += 4+4) {
+ ida->id = IVAL(bp, 0);
+ ida->access = IVAL(bp, 4);
+ }
+ }
+ free(buf);
+ return 0;
+ }
+#endif
+
+ if ((sacl = sys_acl_get_file(fname, type)) != 0) {
+ BOOL ok = unpack_smb_acl(sacl, racl);
+
+ sys_acl_free_acl(sacl);
+ if (!ok) {
+ return -1;
+ }
+ } else if (no_acl_syscall_error(errno)) {
+ /* ACLs are not supported, so pretend we have a basic ACL. */
+ if (type == SMB_ACL_TYPE_ACCESS)
+ rsync_acl_fake_perms(racl, mode);
+ } else {
+ rsyserr(FERROR_XFER, errno, "get_acl: sys_acl_get_file(%s, %s)",
+ fname, str_acl_type(type));
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Return the Access Control List for the given filename. */
+int get_acl(const char *fname, stat_x *sxp)
+{
+ sxp->acc_acl = create_racl();
+
+ if (S_ISREG(sxp->st.st_mode) || S_ISDIR(sxp->st.st_mode)) {
+ /* Everyone supports this. */
+ } else if (S_ISLNK(sxp->st.st_mode)) {
+ return 0;
+ } else if (IS_SPECIAL(sxp->st.st_mode)) {
+#ifndef NO_SPECIAL_ACLS
+ if (!preserve_specials)
+#endif
+ return 0;
+ } else if (IS_DEVICE(sxp->st.st_mode)) {
+#ifndef NO_DEVICE_ACLS
+ if (!preserve_devices)
+#endif
+ return 0;
+ } else if (IS_MISSING_FILE(sxp->st))
+ return 0;
+
+ if (get_rsync_acl(fname, sxp->acc_acl, SMB_ACL_TYPE_ACCESS,
+ sxp->st.st_mode) < 0) {
+ free_acl(sxp);
+ return -1;
+ }
+
+ if (S_ISDIR(sxp->st.st_mode)) {
+ sxp->def_acl = create_racl();
+ if (get_rsync_acl(fname, sxp->def_acl, SMB_ACL_TYPE_DEFAULT,
+ sxp->st.st_mode) < 0) {
+ free_acl(sxp);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/* === Send functions === */
+
+/* Send the ida list over the file descriptor. */
+static void send_ida_entries(int f, const ida_entries *idal)
+{
+ id_access *ida;
+ size_t count = idal->count;
+
+ write_varint(f, idal->count);
+
+ for (ida = idal->idas; count--; ida++) {
+ uint32 xbits = ida->access << 2;
+ const char *name;
+ if (ida->access & NAME_IS_USER) {
+ xbits |= XFLAG_NAME_IS_USER;
+ name = numeric_ids ? NULL : add_uid(ida->id);
+ } else
+ name = numeric_ids ? NULL : add_gid(ida->id);
+ write_varint(f, ida->id);
+ if (inc_recurse && name) {
+ int len = strlen(name);
+ write_varint(f, xbits | XFLAG_NAME_FOLLOWS);
+ write_byte(f, len);
+ write_buf(f, name, len);
+ } else
+ write_varint(f, xbits);
+ }
+}
+
+static void send_rsync_acl(int f, rsync_acl *racl, SMB_ACL_TYPE_T type,
+ item_list *racl_list)
+{
+ int ndx = find_matching_rsync_acl(racl, type, racl_list);
+
+ /* Send 0 (-1 + 1) to indicate that literal ACL data follows. */
+ write_varint(f, ndx + 1);
+
+ if (ndx < 0) {
+ rsync_acl *new_racl = EXPAND_ITEM_LIST(racl_list, rsync_acl, 1000);
+ uchar flags = 0;
+
+ if (racl->user_obj != NO_ENTRY)
+ flags |= XMIT_USER_OBJ;
+ if (racl->group_obj != NO_ENTRY)
+ flags |= XMIT_GROUP_OBJ;
+ if (racl->mask_obj != NO_ENTRY)
+ flags |= XMIT_MASK_OBJ;
+ if (racl->other_obj != NO_ENTRY)
+ flags |= XMIT_OTHER_OBJ;
+ if (racl->names.count)
+ flags |= XMIT_NAME_LIST;
+
+ write_byte(f, flags);
+
+ if (flags & XMIT_USER_OBJ)
+ write_varint(f, racl->user_obj);
+ if (flags & XMIT_GROUP_OBJ)
+ write_varint(f, racl->group_obj);
+ if (flags & XMIT_MASK_OBJ)
+ write_varint(f, racl->mask_obj);
+ if (flags & XMIT_OTHER_OBJ)
+ write_varint(f, racl->other_obj);
+ if (flags & XMIT_NAME_LIST)
+ send_ida_entries(f, &racl->names);
+
+ /* Give the allocated data to the new list object. */
+ *new_racl = *racl;
+ *racl = empty_rsync_acl;
+ }
+}
+
+/* Send the ACL from the stat_x structure down the indicated file descriptor.
+ * This also frees the ACL data. */
+void send_acl(int f, stat_x *sxp)
+{
+ if (!sxp->acc_acl) {
+ sxp->acc_acl = create_racl();
+ rsync_acl_fake_perms(sxp->acc_acl, sxp->st.st_mode);
+ }
+ /* Avoid sending values that can be inferred from other data. */
+ rsync_acl_strip_perms(sxp);
+
+ send_rsync_acl(f, sxp->acc_acl, SMB_ACL_TYPE_ACCESS, &access_acl_list);
+
+ if (S_ISDIR(sxp->st.st_mode)) {
+ if (!sxp->def_acl)
+ sxp->def_acl = create_racl();
+
+ send_rsync_acl(f, sxp->def_acl, SMB_ACL_TYPE_DEFAULT, &default_acl_list);
+ }
+}
+
+/* === Receive functions === */
+
+static uint32 recv_acl_access(int f, uchar *name_follows_ptr)
+{
+ uint32 access = read_varint(f);
+
+ if (name_follows_ptr) {
+ int flags = access & 3;
+ access >>= 2;
+ if (am_root >= 0 && access & ~SMB_ACL_VALID_NAME_BITS)
+ goto value_error;
+ if (flags & XFLAG_NAME_FOLLOWS)
+ *name_follows_ptr = 1;
+ else
+ *name_follows_ptr = 0;
+ if (flags & XFLAG_NAME_IS_USER)
+ access |= NAME_IS_USER;
+ } else if (am_root >= 0 && access & ~SMB_ACL_VALID_OBJ_BITS) {
+ value_error:
+ rprintf(FERROR_XFER, "recv_acl_access: value out of range: %x\n",
+ access);
+ exit_cleanup(RERR_STREAMIO);
+ }
+
+ return access;
+}
+
+static uchar recv_ida_entries(int f, ida_entries *ent)
+{
+ uchar computed_mask_bits = 0;
+ int i, count = read_varint(f);
+
+ if (count) {
+ if (!(ent->idas = new_array(id_access, count)))
+ out_of_memory("recv_ida_entries");
+ } else
+ ent->idas = NULL;
+
+ ent->count = count;
+
+ for (i = 0; i < count; i++) {
+ uchar has_name;
+ id_t id = read_varint(f);
+ uint32 access = recv_acl_access(f, &has_name);
+
+ if (has_name) {
+ if (access & NAME_IS_USER)
+ id = recv_user_name(f, id);
+ else
+ id = recv_group_name(f, id, NULL);
+ } else if (access & NAME_IS_USER) {
+ if (inc_recurse && am_root && !numeric_ids)
+ id = match_uid(id);
+ } else {
+ if (inc_recurse && (!am_root || !numeric_ids))
+ id = match_gid(id, NULL);
+ }
+
+ ent->idas[i].id = id;
+ ent->idas[i].access = access;
+ computed_mask_bits |= access;
+ }
+
+ return computed_mask_bits & ~NO_ENTRY;
+}
+
+static int recv_rsync_acl(int f, item_list *racl_list, SMB_ACL_TYPE_T type, mode_t mode)
+{
+ uchar computed_mask_bits = 0;
+ acl_duo *duo_item;
+ uchar flags;
+ int ndx = read_varint(f);
+
+ if (ndx < 0 || (size_t)ndx > racl_list->count) {
+ rprintf(FERROR_XFER, "recv_acl_index: %s ACL index %d > %d\n",
+ str_acl_type(type), ndx, (int)racl_list->count);
+ exit_cleanup(RERR_STREAMIO);
+ }
+
+ if (ndx != 0)
+ return ndx - 1;
+
+ ndx = racl_list->count;
+ duo_item = EXPAND_ITEM_LIST(racl_list, acl_duo, 1000);
+ duo_item->racl = empty_rsync_acl;
+
+ flags = read_byte(f);
+
+ if (flags & XMIT_USER_OBJ)
+ duo_item->racl.user_obj = recv_acl_access(f, NULL);
+ if (flags & XMIT_GROUP_OBJ)
+ duo_item->racl.group_obj = recv_acl_access(f, NULL);
+ if (flags & XMIT_MASK_OBJ)
+ duo_item->racl.mask_obj = recv_acl_access(f, NULL);
+ if (flags & XMIT_OTHER_OBJ)
+ duo_item->racl.other_obj = recv_acl_access(f, NULL);
+ if (flags & XMIT_NAME_LIST)
+ computed_mask_bits |= recv_ida_entries(f, &duo_item->racl.names);
+
+#ifdef HAVE_OSX_ACLS
+ /* If we received a superfluous mask, throw it away. */
+ duo_item->racl.mask_obj = NO_ENTRY;
+#else
+ if (duo_item->racl.names.count && duo_item->racl.mask_obj == NO_ENTRY) {
+ /* Mask must be non-empty with lists. */
+ if (type == SMB_ACL_TYPE_ACCESS)
+ computed_mask_bits = (mode >> 3) & 7;
+ else
+ computed_mask_bits |= duo_item->racl.group_obj & ~NO_ENTRY;
+ duo_item->racl.mask_obj = computed_mask_bits;
+ }
+#endif
+
+ duo_item->sacl = NULL;
+
+ return ndx;
+}
+
+/* Receive the ACL info the sender has included for this file-list entry. */
+void receive_acl(int f, struct file_struct *file)
+{
+ F_ACL(file) = recv_rsync_acl(f, &access_acl_list, SMB_ACL_TYPE_ACCESS, file->mode);
+
+ if (S_ISDIR(file->mode))
+ F_DIR_DEFACL(file) = recv_rsync_acl(f, &default_acl_list, SMB_ACL_TYPE_DEFAULT, 0);
+}
+
+static int cache_rsync_acl(rsync_acl *racl, SMB_ACL_TYPE_T type, item_list *racl_list)
+{
+ int ndx;
+
+ if (!racl)
+ ndx = -1;
+ else if ((ndx = find_matching_rsync_acl(racl, type, racl_list)) == -1) {
+ acl_duo *new_duo;
+ ndx = racl_list->count;
+ new_duo = EXPAND_ITEM_LIST(racl_list, acl_duo, 1000);
+ new_duo->racl = *racl;
+ new_duo->sacl = NULL;
+ *racl = empty_rsync_acl;
+ }
+
+ return ndx;
+}
+
+/* Turn the ACL data in stat_x into cached ACL data, setting the index
+ * values in the file struct. */
+void cache_tmp_acl(struct file_struct *file, stat_x *sxp)
+{
+ if (prior_access_count == (size_t)-1)
+ prior_access_count = access_acl_list.count;
+
+ F_ACL(file) = cache_rsync_acl(sxp->acc_acl,
+ SMB_ACL_TYPE_ACCESS, &access_acl_list);
+
+ if (S_ISDIR(sxp->st.st_mode)) {
+ if (prior_default_count == (size_t)-1)
+ prior_default_count = default_acl_list.count;
+ F_DIR_DEFACL(file) = cache_rsync_acl(sxp->def_acl,
+ SMB_ACL_TYPE_DEFAULT, &default_acl_list);
+ }
+}
+
+static void uncache_duo_acls(item_list *duo_list, size_t start)
+{
+ acl_duo *duo_item = duo_list->items;
+ acl_duo *duo_start = duo_item + start;
+
+ duo_item += duo_list->count;
+ duo_list->count = start;
+
+ while (duo_item-- > duo_start) {
+ rsync_acl_free(&duo_item->racl);
+ if (duo_item->sacl)
+ sys_acl_free_acl(duo_item->sacl);
+ }
+}
+
+void uncache_tmp_acls(void)
+{
+ if (prior_access_count != (size_t)-1) {
+ uncache_duo_acls(&access_acl_list, prior_access_count);
+ prior_access_count = (size_t)-1;
+ }
+
+ if (prior_default_count != (size_t)-1) {
+ uncache_duo_acls(&default_acl_list, prior_default_count);
+ prior_default_count = (size_t)-1;
+ }
+}
+
+#ifndef HAVE_OSX_ACLS
+static mode_t change_sacl_perms(SMB_ACL_T sacl, rsync_acl *racl, mode_t old_mode, mode_t mode)
+{
+ SMB_ACL_ENTRY_T entry;
+ const char *errfun;
+ int rc;
+
+ if (S_ISDIR(mode)) {
+ /* If the sticky bit is going on, it's not safe to allow all
+ * the new ACL to go into effect before it gets set. */
+#ifdef SMB_ACL_LOSES_SPECIAL_MODE_BITS
+ if (mode & S_ISVTX)
+ mode &= ~0077;
+#else
+ if (mode & S_ISVTX && !(old_mode & S_ISVTX))
+ mode &= ~0077;
+ } else {
+ /* If setuid or setgid is going off, it's not safe to allow all
+ * the new ACL to go into effect before they get cleared. */
+ if ((old_mode & S_ISUID && !(mode & S_ISUID))
+ || (old_mode & S_ISGID && !(mode & S_ISGID)))
+ mode &= ~0077;
+#endif
+ }
+
+ errfun = "sys_acl_get_entry";
+ for (rc = sys_acl_get_entry(sacl, SMB_ACL_FIRST_ENTRY, &entry);
+ rc == 1;
+ rc = sys_acl_get_entry(sacl, SMB_ACL_NEXT_ENTRY, &entry)) {
+ SMB_ACL_TAG_T tag_type;
+ if ((rc = sys_acl_get_tag_type(entry, &tag_type)) != 0) {
+ errfun = "sys_acl_get_tag_type";
+ break;
+ }
+ switch (tag_type) {
+ case SMB_ACL_USER_OBJ:
+ COE2( store_access_in_entry,((mode >> 6) & 7, entry) );
+ break;
+ case SMB_ACL_GROUP_OBJ:
+ /* group is only empty when identical to group perms. */
+ if (racl->group_obj != NO_ENTRY)
+ break;
+ COE2( store_access_in_entry,((mode >> 3) & 7, entry) );
+ break;
+ case SMB_ACL_MASK:
+#ifndef HAVE_SOLARIS_ACLS
+#ifndef ACLS_NEED_MASK
+ /* mask is only empty when we don't need it. */
+ if (racl->mask_obj == NO_ENTRY)
+ break;
+#endif
+ COE2( store_access_in_entry,((mode >> 3) & 7, entry) );
+#endif
+ break;
+ case SMB_ACL_OTHER:
+ COE2( store_access_in_entry,(mode & 7, entry) );
+ break;
+ }
+ }
+ if (rc) {
+ error_exit:
+ if (errfun) {
+ rsyserr(FERROR_XFER, errno, "change_sacl_perms: %s()",
+ errfun);
+ }
+ return (mode_t)-1;
+ }
+
+#ifdef SMB_ACL_LOSES_SPECIAL_MODE_BITS
+ /* Ensure that chmod() will be called to restore any lost setid bits. */
+ if (old_mode & (S_ISUID | S_ISGID | S_ISVTX)
+ && BITS_EQUAL(old_mode, mode, CHMOD_BITS))
+ old_mode &= ~(S_ISUID | S_ISGID | S_ISVTX);
+#endif
+
+ /* Return the mode of the file on disk, as we will set them. */
+ return (old_mode & ~ACCESSPERMS) | (mode & ACCESSPERMS);
+}
+#endif
+
+static int set_rsync_acl(const char *fname, acl_duo *duo_item,
+ SMB_ACL_TYPE_T type, stat_x *sxp, mode_t mode)
+{
+ if (type == SMB_ACL_TYPE_DEFAULT
+ && duo_item->racl.user_obj == NO_ENTRY) {
+ int rc;
+#ifdef SUPPORT_XATTRS
+ /* --fake-super support: delete default ACL from xattrs. */
+ if (am_root < 0)
+ rc = del_def_xattr_acl(fname);
+ else
+#endif
+ rc = sys_acl_delete_def_file(fname);
+ if (rc < 0) {
+ rsyserr(FERROR_XFER, errno, "set_acl: sys_acl_delete_def_file(%s)",
+ fname);
+ return -1;
+ }
+#ifdef SUPPORT_XATTRS
+ } else if (am_root < 0) {
+ /* --fake-super support: store ACLs in an xattr. */
+ int cnt = duo_item->racl.names.count;
+ size_t len = 4*4 + cnt * (4+4);
+ char *buf = new_array(char, len);
+ int rc;
+
+ SIVAL(buf, 0, duo_item->racl.user_obj);
+ SIVAL(buf, 4, duo_item->racl.group_obj);
+ SIVAL(buf, 8, duo_item->racl.mask_obj);
+ SIVAL(buf, 12, duo_item->racl.other_obj);
+
+ if (cnt) {
+ char *bp = buf + 4*4;
+ id_access *ida = duo_item->racl.names.idas;
+ for ( ; cnt--; ida++, bp += 4+4) {
+ SIVAL(bp, 0, ida->id);
+ SIVAL(bp, 4, ida->access);
+ }
+ }
+ rc = set_xattr_acl(fname, type == SMB_ACL_TYPE_ACCESS, buf, len);
+ free(buf);
+ return rc;
+#endif
+ } else {
+ mode_t cur_mode = sxp->st.st_mode;
+ if (!duo_item->sacl
+ && !pack_smb_acl(&duo_item->sacl, &duo_item->racl))
+ return -1;
+#ifdef HAVE_OSX_ACLS
+ mode = 0; /* eliminate compiler warning */
+#else
+ if (type == SMB_ACL_TYPE_ACCESS) {
+ cur_mode = change_sacl_perms(duo_item->sacl, &duo_item->racl,
+ cur_mode, mode);
+ if (cur_mode == (mode_t)-1)
+ return 0;
+ }
+#endif
+ if (sys_acl_set_file(fname, type, duo_item->sacl) < 0) {
+ rsyserr(FERROR_XFER, errno, "set_acl: sys_acl_set_file(%s, %s)",
+ fname, str_acl_type(type));
+ return -1;
+ }
+ if (type == SMB_ACL_TYPE_ACCESS)
+ sxp->st.st_mode = cur_mode;
+ }
+
+ return 0;
+}
+
+/* Given a fname, this sets extended access ACL entries, the default ACL (for a
+ * dir), and the regular mode bits on the file. Call this with fname set to
+ * NULL to just check if the ACL is different.
+ *
+ * If the ACL operation has a side-effect of changing the file's mode, the
+ * sxp->st.st_mode value will be changed to match.
+ *
+ * Returns 0 for an unchanged ACL, 1 for changed, -1 for failed. */
+int set_acl(const char *fname, const struct file_struct *file, stat_x *sxp, mode_t new_mode)
+{
+ int changed = 0;
+ int32 ndx;
+ BOOL eq;
+
+ if (!dry_run && (read_only || list_only)) {
+ errno = EROFS;
+ return -1;
+ }
+
+ ndx = F_ACL(file);
+ if (ndx >= 0 && (size_t)ndx < access_acl_list.count) {
+ acl_duo *duo_item = access_acl_list.items;
+ duo_item += ndx;
+ eq = sxp->acc_acl
+ && rsync_acl_equal_enough(sxp->acc_acl, &duo_item->racl, new_mode);
+ if (!eq) {
+ changed = 1;
+ if (!dry_run && fname
+ && set_rsync_acl(fname, duo_item, SMB_ACL_TYPE_ACCESS,
+ sxp, new_mode) < 0)
+ return -1;
+ }
+ }
+
+ if (!S_ISDIR(new_mode))
+ return changed;
+
+ ndx = F_DIR_DEFACL(file);
+ if (ndx >= 0 && (size_t)ndx < default_acl_list.count) {
+ acl_duo *duo_item = default_acl_list.items;
+ duo_item += ndx;
+ eq = sxp->def_acl && rsync_acl_equal(sxp->def_acl, &duo_item->racl);
+ if (!eq) {
+ changed = 1;
+ if (!dry_run && fname
+ && set_rsync_acl(fname, duo_item, SMB_ACL_TYPE_DEFAULT,
+ sxp, new_mode) < 0)
+ return -1;
+ }
+ }
+
+ return changed;
+}
+
+/* Non-incremental recursion needs to convert all the received IDs.
+ * This is done in a single pass after receiving the whole file-list. */
+static void match_racl_ids(const item_list *racl_list)
+{
+ int list_cnt, name_cnt;
+ acl_duo *duo_item = racl_list->items;
+ for (list_cnt = racl_list->count; list_cnt--; duo_item++) {
+ ida_entries *idal = &duo_item->racl.names;
+ id_access *ida = idal->idas;
+ for (name_cnt = idal->count; name_cnt--; ida++) {
+ if (ida->access & NAME_IS_USER)
+ ida->id = match_uid(ida->id);
+ else
+ ida->id = match_gid(ida->id, NULL);
+ }
+ }
+}
+
+void match_acl_ids(void)
+{
+ match_racl_ids(&access_acl_list);
+ match_racl_ids(&default_acl_list);
+}
+
+/* This is used by dest_mode(). */
+int default_perms_for_dir(const char *dir)
+{
+ rsync_acl racl;
+ SMB_ACL_T sacl;
+ BOOL ok;
+ int perms;
+
+ if (dir == NULL)
+ dir = ".";
+ perms = ACCESSPERMS & ~orig_umask;
+ /* Read the directory's default ACL. If it has none, this will successfully return an empty ACL. */
+ sacl = sys_acl_get_file(dir, SMB_ACL_TYPE_DEFAULT);
+ if (sacl == NULL) {
+ /* Couldn't get an ACL. Darn. */
+ switch (errno) {
+ case EINVAL:
+ /* If SMB_ACL_TYPE_DEFAULT isn't valid, then the ACLs must be non-POSIX. */
+ break;
+#ifdef ENOTSUP
+ case ENOTSUP:
+#endif
+ case ENOSYS:
+ /* No ACLs are available. */
+ break;
+ case ENOENT:
+ if (dry_run) {
+ /* We're doing a dry run, so the containing directory
+ * wasn't actually created. Don't worry about it. */
+ break;
+ }
+ /* Otherwise fall through. */
+ default:
+ rprintf(FWARNING,
+ "default_perms_for_dir: sys_acl_get_file(%s, %s): %s, falling back on umask\n",
+ dir, str_acl_type(SMB_ACL_TYPE_DEFAULT), strerror(errno));
+ }
+ return perms;
+ }
+
+ /* Convert it. */
+ racl = empty_rsync_acl;
+ ok = unpack_smb_acl(sacl, &racl);
+ sys_acl_free_acl(sacl);
+ if (!ok) {
+ rprintf(FWARNING, "default_perms_for_dir: unpack_smb_acl failed, falling back on umask\n");
+ return perms;
+ }
+
+ /* Apply the permission-bit entries of the default ACL, if any. */
+ if (racl.user_obj != NO_ENTRY) {
+ perms = rsync_acl_get_perms(&racl);
+ if (DEBUG_GTE(ACL, 1))
+ rprintf(FINFO, "got ACL-based default perms %o for directory %s\n", perms, dir);
+ }
+
+ rsync_acl_free(&racl);
+ return perms;
+}
+
+#endif /* SUPPORT_ACLS */
diff --git a/rsync/authenticate.c b/rsync/authenticate.c
new file mode 100644
index 0000000..c92746c
--- /dev/null
+++ b/rsync/authenticate.c
@@ -0,0 +1,373 @@
+/*
+ * Support rsync daemon authentication.
+ *
+ * Copyright (C) 1998-2000 Andrew Tridgell
+ * Copyright (C) 2002-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "itypes.h"
+
+extern int read_only;
+extern char *password_file;
+
+/***************************************************************************
+encode a buffer using base64 - simple and slow algorithm. null terminates
+the result.
+ ***************************************************************************/
+void base64_encode(const char *buf, int len, char *out, int pad)
+{
+ char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ int bit_offset, byte_offset, idx, i;
+ const uchar *d = (const uchar *)buf;
+ int bytes = (len*8 + 5)/6;
+
+ for (i = 0; i < bytes; i++) {
+ byte_offset = (i*6)/8;
+ bit_offset = (i*6)%8;
+ if (bit_offset < 3) {
+ idx = (d[byte_offset] >> (2-bit_offset)) & 0x3F;
+ } else {
+ idx = (d[byte_offset] << (bit_offset-2)) & 0x3F;
+ if (byte_offset+1 < len) {
+ idx |= (d[byte_offset+1] >> (8-(bit_offset-2)));
+ }
+ }
+ out[i] = b64[idx];
+ }
+
+ while (pad && (i % 4))
+ out[i++] = '=';
+
+ out[i] = '\0';
+}
+
+/* Generate a challenge buffer and return it base64-encoded. */
+static void gen_challenge(const char *addr, char *challenge)
+{
+ char input[32];
+ char digest[MAX_DIGEST_LEN];
+ struct timeval tv;
+ int len;
+
+ memset(input, 0, sizeof input);
+
+ strlcpy(input, addr, 17);
+ sys_gettimeofday(&tv);
+ SIVAL(input, 16, tv.tv_sec);
+ SIVAL(input, 20, tv.tv_usec);
+ SIVAL(input, 24, getpid());
+
+ sum_init(0);
+ sum_update(input, sizeof input);
+ len = sum_end(digest);
+
+ base64_encode(digest, len, challenge, 0);
+}
+
+/* Generate an MD4 hash created from the combination of the password
+ * and the challenge string and return it base64-encoded. */
+static void generate_hash(const char *in, const char *challenge, char *out)
+{
+ char buf[MAX_DIGEST_LEN];
+ int len;
+
+ sum_init(0);
+ sum_update(in, strlen(in));
+ sum_update(challenge, strlen(challenge));
+ len = sum_end(buf);
+
+ base64_encode(buf, len, out, 0);
+}
+
+/* Return the secret for a user from the secret file, null terminated.
+ * Maximum length is len (not counting the null). */
+static const char *check_secret(int module, const char *user, const char *group,
+ const char *challenge, const char *pass)
+{
+ char line[1024];
+ char pass2[MAX_DIGEST_LEN*2];
+ const char *fname = lp_secrets_file(module);
+ STRUCT_STAT st;
+ int ok = 1;
+ int user_len = strlen(user);
+ int group_len = group ? strlen(group) : 0;
+ char *err;
+ FILE *fh;
+
+ if (!fname || !*fname || (fh = fopen(fname, "r")) == NULL)
+ return "no secrets file";
+
+ if (do_fstat(fileno(fh), &st) == -1) {
+ rsyserr(FLOG, errno, "fstat(%s)", fname);
+ ok = 0;
+ } else if (lp_strict_modes(module)) {
+ if ((st.st_mode & 06) != 0) {
+ rprintf(FLOG, "secrets file must not be other-accessible (see strict modes option)\n");
+ ok = 0;
+ } else if (MY_UID() == 0 && st.st_uid != 0) {
+ rprintf(FLOG, "secrets file must be owned by root when running as root (see strict modes)\n");
+ ok = 0;
+ }
+ }
+ if (!ok) {
+ fclose(fh);
+ return "ignoring secrets file";
+ }
+
+ if (*user == '#') {
+ /* Reject attempt to match a comment. */
+ fclose(fh);
+ return "invalid username";
+ }
+
+ /* Try to find a line that starts with the user (or @group) name and a ':'. */
+ err = "secret not found";
+ while ((user || group) && fgets(line, sizeof line, fh) != NULL) {
+ const char **ptr, *s = strtok(line, "\n\r");
+ int len;
+ if (!s)
+ continue;
+ if (*s == '@') {
+ ptr = &group;
+ len = group_len;
+ s++;
+ } else {
+ ptr = &user;
+ len = user_len;
+ }
+ if (!*ptr || strncmp(s, *ptr, len) != 0 || s[len] != ':')
+ continue;
+ generate_hash(s+len+1, challenge, pass2);
+ if (strcmp(pass, pass2) == 0) {
+ err = NULL;
+ break;
+ }
+ err = "password mismatch";
+ *ptr = NULL; /* Don't look for name again. */
+ }
+
+ fclose(fh);
+
+ memset(line, 0, sizeof line);
+ memset(pass2, 0, sizeof pass2);
+
+ return err;
+}
+
+static const char *getpassf(const char *filename)
+{
+ STRUCT_STAT st;
+ char buffer[512], *p;
+ int n;
+
+ if (!filename)
+ return NULL;
+
+ if (strcmp(filename, "-") == 0) {
+ n = fgets(buffer, sizeof buffer, stdin) == NULL ? -1 : (int)strlen(buffer);
+ } else {
+ int fd;
+
+ if ((fd = open(filename,O_RDONLY)) < 0) {
+ rsyserr(FERROR, errno, "could not open password file %s", filename);
+ exit_cleanup(RERR_SYNTAX);
+ }
+
+ if (do_stat(filename, &st) == -1) {
+ rsyserr(FERROR, errno, "stat(%s)", filename);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if ((st.st_mode & 06) != 0) {
+ rprintf(FERROR, "ERROR: password file must not be other-accessible\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (MY_UID() == 0 && st.st_uid != 0) {
+ rprintf(FERROR, "ERROR: password file must be owned by root when running as root\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+
+ n = read(fd, buffer, sizeof buffer - 1);
+ close(fd);
+ }
+
+ if (n > 0) {
+ buffer[n] = '\0';
+ if ((p = strtok(buffer, "\n\r")) != NULL)
+ return strdup(p);
+ }
+
+ rprintf(FERROR, "ERROR: failed to read a password from %s\n", filename);
+ exit_cleanup(RERR_SYNTAX);
+}
+
+/* Possibly negotiate authentication with the client. Use "leader" to
+ * start off the auth if necessary.
+ *
+ * Return NULL if authentication failed. Return "" if anonymous access.
+ * Otherwise return username.
+ */
+char *auth_server(int f_in, int f_out, int module, const char *host,
+ const char *addr, const char *leader)
+{
+ char *users = lp_auth_users(module);
+ char challenge[MAX_DIGEST_LEN*2];
+ char line[BIGPATHBUFLEN];
+ char **auth_uid_groups = NULL;
+ int auth_uid_groups_cnt = -1;
+ const char *err = NULL;
+ int group_match = -1;
+ char *tok, *pass;
+ char opt_ch = '\0';
+
+ /* if no auth list then allow anyone in! */
+ if (!users || !*users)
+ return "";
+
+ gen_challenge(addr, challenge);
+
+ io_printf(f_out, "%s%s\n", leader, challenge);
+
+ if (!read_line_old(f_in, line, sizeof line, 0)
+ || (pass = strchr(line, ' ')) == NULL) {
+ rprintf(FLOG, "auth failed on module %s from %s (%s): "
+ "invalid challenge response\n",
+ lp_name(module), host, addr);
+ return NULL;
+ }
+ *pass++ = '\0';
+
+ if (!(users = strdup(users)))
+ out_of_memory("auth_server");
+
+ for (tok = strtok(users, " ,\t"); tok; tok = strtok(NULL, " ,\t")) {
+ char *opts;
+ /* See if the user appended :deny, :ro, or :rw. */
+ if ((opts = strchr(tok, ':')) != NULL) {
+ *opts++ = '\0';
+ opt_ch = isUpper(opts) ? toLower(opts) : *opts;
+ if (opt_ch == 'r') { /* handle ro and rw */
+ opt_ch = isUpper(opts+1) ? toLower(opts+1) : opts[1];
+ if (opt_ch == 'o')
+ opt_ch = 'r';
+ else if (opt_ch != 'w')
+ opt_ch = '\0';
+ } else if (opt_ch != 'd') /* if it's not deny, ignore it */
+ opt_ch = '\0';
+ } else
+ opt_ch = '\0';
+ if (*tok != '@') {
+ /* Match the username */
+ if (wildmatch(tok, line))
+ break;
+ } else {
+#ifdef HAVE_GETGROUPLIST
+ int j;
+ /* See if authorizing user is a real user, and if so, see
+ * if it is in a group that matches tok+1 wildmat. */
+ if (auth_uid_groups_cnt < 0) {
+ gid_t gid_list[64];
+ uid_t auth_uid;
+ auth_uid_groups_cnt = sizeof gid_list / sizeof (gid_t);
+ if (!user_to_uid(line, &auth_uid, False)
+ || getallgroups(auth_uid, gid_list, &auth_uid_groups_cnt) != NULL)
+ auth_uid_groups_cnt = 0;
+ else {
+ if ((auth_uid_groups = new_array(char *, auth_uid_groups_cnt)) == NULL)
+ out_of_memory("auth_server");
+ for (j = 0; j < auth_uid_groups_cnt; j++)
+ auth_uid_groups[j] = gid_to_group(gid_list[j]);
+ }
+ }
+ for (j = 0; j < auth_uid_groups_cnt; j++) {
+ if (auth_uid_groups[j] && wildmatch(tok+1, auth_uid_groups[j])) {
+ group_match = j;
+ break;
+ }
+ }
+ if (group_match >= 0)
+ break;
+#else
+ rprintf(FLOG, "your computer doesn't support getgrouplist(), so no @group authorization is possible.\n");
+#endif
+ }
+ }
+
+ free(users);
+
+ if (!tok)
+ err = "no matching rule";
+ else if (opt_ch == 'd')
+ err = "denied by rule";
+ else {
+ char *group = group_match >= 0 ? auth_uid_groups[group_match] : NULL;
+ err = check_secret(module, line, group, challenge, pass);
+ }
+
+ memset(challenge, 0, sizeof challenge);
+ memset(pass, 0, strlen(pass));
+
+ if (auth_uid_groups) {
+ int j;
+ for (j = 0; j < auth_uid_groups_cnt; j++) {
+ if (auth_uid_groups[j])
+ free(auth_uid_groups[j]);
+ }
+ free(auth_uid_groups);
+ }
+
+ if (err) {
+ rprintf(FLOG, "auth failed on module %s from %s (%s) for %s: %s\n",
+ lp_name(module), host, addr, line, err);
+ return NULL;
+ }
+
+ if (opt_ch == 'r')
+ read_only = 1;
+ else if (opt_ch == 'w')
+ read_only = 0;
+
+ return strdup(line);
+}
+
+void auth_client(int fd, const char *user, const char *challenge)
+{
+ const char *pass;
+ char pass2[MAX_DIGEST_LEN*2];
+
+ if (!user || !*user)
+ user = "nobody";
+
+ if (!(pass = getpassf(password_file))
+ && !(pass = getenv("RSYNC_PASSWORD"))) {
+ /* XXX: cyeoh says that getpass is deprecated, because
+ * it may return a truncated password on some systems,
+ * and it is not in the LSB.
+ *
+ * Andrew Klein says that getpassphrase() is present
+ * on Solaris and reads up to 256 characters.
+ *
+ * OpenBSD has a readpassphrase() that might be more suitable.
+ */
+ pass = getpass("Password: ");
+ }
+
+ if (!pass)
+ pass = "";
+
+ generate_hash(pass, challenge, pass2);
+ io_printf(fd, "%s %s\n", user, pass2);
+}
diff --git a/rsync/backup.c b/rsync/backup.c
new file mode 100644
index 0000000..8987723
--- /dev/null
+++ b/rsync/backup.c
@@ -0,0 +1,341 @@
+/*
+ * Backup handling code.
+ *
+ * Copyright (C) 1999 Andrew Tridgell
+ * Copyright (C) 2003-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "ifuncs.h"
+
+extern int am_root;
+extern int preserve_acls;
+extern int preserve_xattrs;
+extern int preserve_devices;
+extern int preserve_specials;
+extern int preserve_links;
+extern int safe_symlinks;
+extern int backup_dir_len;
+extern unsigned int backup_dir_remainder;
+extern char backup_dir_buf[MAXPATHLEN];
+extern char *backup_suffix;
+extern char *backup_dir;
+
+/* Returns -1 on error, 0 on missing dir, and 1 on present dir. */
+static int validate_backup_dir(void)
+{
+ STRUCT_STAT st;
+
+ if (do_lstat(backup_dir_buf, &st) < 0) {
+ if (errno == ENOENT)
+ return 0;
+ rsyserr(FERROR, errno, "backup lstat %s failed", backup_dir_buf);
+ return -1;
+ }
+ if (!S_ISDIR(st.st_mode)) {
+ int flags = get_del_for_flag(st.st_mode) | DEL_FOR_BACKUP | DEL_RECURSE;
+ if (delete_item(backup_dir_buf, st.st_mode, flags) == 0)
+ return 0;
+ return -1;
+ }
+ return 1;
+}
+
+/* Create a backup path from the given fname, putting the result into
+ * backup_dir_buf. Any new directories (compared to the prior backup
+ * path) are ensured to exist as directories, replacing anything else
+ * that may be in the way (e.g. a symlink). */
+static BOOL copy_valid_path(const char *fname)
+{
+ const char *f;
+ int val;
+ BOOL ret = True;
+ stat_x sx;
+ char *b, *rel = backup_dir_buf + backup_dir_len, *name = rel;
+
+ for (f = fname, b = rel; *f && *f == *b; f++, b++) {
+ if (*b == '/')
+ name = b + 1;
+ }
+
+ if (stringjoin(rel, backup_dir_remainder, fname, backup_suffix, NULL) >= backup_dir_remainder) {
+ rprintf(FERROR, "backup filename too long\n");
+ *name = '\0';
+ return False;
+ }
+
+ for ( ; ; name = b + 1) {
+ if ((b = strchr(name, '/')) == NULL)
+ return True;
+ *b = '\0';
+
+ val = validate_backup_dir();
+ if (val == 0)
+ break;
+ if (val < 0) {
+ *name = '\0';
+ return False;
+ }
+
+ *b = '/';
+ }
+
+ init_stat_x(&sx);
+
+ for ( ; b; name = b + 1, b = strchr(name, '/')) {
+ *b = '\0';
+
+ while (do_mkdir(backup_dir_buf, ACCESSPERMS) < 0) {
+ if (errno == EEXIST) {
+ val = validate_backup_dir();
+ if (val > 0)
+ break;
+ if (val == 0)
+ continue;
+ } else
+ rsyserr(FERROR, errno, "backup mkdir %s failed", backup_dir_buf);
+ *name = '\0';
+ ret = False;
+ goto cleanup;
+ }
+
+ /* Try to transfer the directory settings of the actual dir
+ * that the files are coming from. */
+ if (x_stat(rel, &sx.st, NULL) < 0)
+ rsyserr(FERROR, errno, "backup stat %s failed", full_fname(rel));
+ else {
+ struct file_struct *file;
+ if (!(file = make_file(rel, NULL, NULL, 0, NO_FILTERS)))
+ continue;
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && !S_ISLNK(file->mode)) {
+ get_acl(rel, &sx);
+ cache_tmp_acl(file, &sx);
+ free_acl(&sx);
+ }
+#endif
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs) {
+ get_xattr(rel, &sx);
+ cache_tmp_xattr(file, &sx);
+ free_xattr(&sx);
+ }
+#endif
+ set_file_attrs(backup_dir_buf, file, NULL, NULL, 0);
+ unmake_file(file);
+ }
+
+ *b = '/';
+ }
+
+ cleanup:
+
+#ifdef SUPPORT_ACLS
+ uncache_tmp_acls();
+#endif
+#ifdef SUPPORT_XATTRS
+ uncache_tmp_xattrs();
+#endif
+
+ return ret;
+}
+
+/* Make a complete pathname for backup file and verify any new path elements. */
+char *get_backup_name(const char *fname)
+{
+ if (backup_dir) {
+ /* copy fname into backup_dir_buf while validating the dirs. */
+ if (copy_valid_path(fname))
+ return backup_dir_buf;
+ /* copy_valid_path() has printed an error message. */
+ return NULL;
+ }
+
+ if (stringjoin(backup_dir_buf, MAXPATHLEN, fname, backup_suffix, NULL) < MAXPATHLEN)
+ return backup_dir_buf;
+
+ rprintf(FERROR, "backup filename too long\n");
+ return NULL;
+}
+
+/* Has same return codes as make_backup(). */
+static inline int link_or_rename(const char *from, const char *to,
+ BOOL prefer_rename, STRUCT_STAT *stp)
+{
+#ifdef SUPPORT_HARD_LINKS
+ if (!prefer_rename) {
+#ifndef CAN_HARDLINK_SYMLINK
+ if (S_ISLNK(stp->st_mode))
+ return 0; /* Use copy code. */
+#endif
+#ifndef CAN_HARDLINK_SPECIAL
+ if (IS_SPECIAL(stp->st_mode) || IS_DEVICE(stp->st_mode))
+ return 0; /* Use copy code. */
+#endif
+ if (do_link(from, to) == 0) {
+ if (DEBUG_GTE(BACKUP, 1))
+ rprintf(FINFO, "make_backup: HLINK %s successful.\n", from);
+ return 2;
+ }
+ /* We prefer to rename a regular file rather than copy it. */
+ if (!S_ISREG(stp->st_mode) || errno == EEXIST || errno == EISDIR)
+ return 0;
+ }
+#endif
+ if (do_rename(from, to) == 0) {
+ if (stp->st_nlink > 1 && !S_ISDIR(stp->st_mode)) {
+ /* If someone has hard-linked the file into the backup
+ * dir, rename() might return success but do nothing! */
+ robust_unlink(from); /* Just in case... */
+ }
+ if (DEBUG_GTE(BACKUP, 1))
+ rprintf(FINFO, "make_backup: RENAME %s successful.\n", from);
+ return 1;
+ }
+ return 0;
+}
+
+/* Hard-link, rename, or copy an item to the backup name. Returns 2 if item
+ * was duplicated into backup area, 1 if item was moved, or 0 for failure.*/
+int make_backup(const char *fname, BOOL prefer_rename)
+{
+ stat_x sx;
+ struct file_struct *file;
+ int save_preserve_xattrs;
+ char *buf = get_backup_name(fname);
+ int ret = 0;
+
+ if (!buf)
+ return 0;
+
+ init_stat_x(&sx);
+ /* Return success if no file to keep. */
+ if (x_lstat(fname, &sx.st, NULL) < 0)
+ return 1;
+
+ /* Try a hard-link or a rename first. Using rename is not atomic, but
+ * is more efficient than forcing a copy for larger files when no hard-
+ * linking is possible. */
+ if ((ret = link_or_rename(fname, buf, prefer_rename, &sx.st)) != 0)
+ goto success;
+ if (errno == EEXIST || errno == EISDIR) {
+ STRUCT_STAT bakst;
+ if (do_lstat(buf, &bakst) == 0) {
+ int flags = get_del_for_flag(bakst.st_mode) | DEL_FOR_BACKUP | DEL_RECURSE;
+ if (delete_item(buf, bakst.st_mode, flags) != 0)
+ return 0;
+ }
+ if ((ret = link_or_rename(fname, buf, prefer_rename, &sx.st)) != 0)
+ goto success;
+ }
+
+ /* Fall back to making a copy. */
+ if (!(file = make_file(fname, NULL, &sx.st, 0, NO_FILTERS)))
+ return 1; /* the file could have disappeared */
+
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && !S_ISLNK(file->mode)) {
+ get_acl(fname, &sx);
+ cache_tmp_acl(file, &sx);
+ free_acl(&sx);
+ }
+#endif
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs) {
+ get_xattr(fname, &sx);
+ cache_tmp_xattr(file, &sx);
+ free_xattr(&sx);
+ }
+#endif
+
+ /* Check to see if this is a device file, or link */
+ if ((am_root && preserve_devices && IS_DEVICE(file->mode))
+ || (preserve_specials && IS_SPECIAL(file->mode))) {
+ if (do_mknod(buf, file->mode, sx.st.st_rdev) < 0)
+ rsyserr(FERROR, errno, "mknod %s failed", full_fname(buf));
+ else if (DEBUG_GTE(BACKUP, 1))
+ rprintf(FINFO, "make_backup: DEVICE %s successful.\n", fname);
+ ret = 2;
+ }
+
+#ifdef SUPPORT_LINKS
+ if (!ret && preserve_links && S_ISLNK(file->mode)) {
+ const char *sl = F_SYMLINK(file);
+ if (safe_symlinks && unsafe_symlink(sl, fname)) {
+ if (INFO_GTE(SYMSAFE, 1)) {
+ rprintf(FINFO, "not backing up unsafe symlink \"%s\" -> \"%s\"\n",
+ fname, sl);
+ }
+ ret = 2;
+ } else {
+ if (do_symlink(sl, buf) < 0)
+ rsyserr(FERROR, errno, "link %s -> \"%s\"", full_fname(buf), sl);
+ else if (DEBUG_GTE(BACKUP, 1))
+ rprintf(FINFO, "make_backup: SYMLINK %s successful.\n", fname);
+ ret = 2;
+ }
+ }
+#endif
+
+ if (!ret && !S_ISREG(file->mode)) {
+ rprintf(FINFO, "make_bak: skipping non-regular file %s\n", fname);
+ unmake_file(file);
+#ifdef SUPPORT_ACLS
+ uncache_tmp_acls();
+#endif
+#ifdef SUPPORT_XATTRS
+ uncache_tmp_xattrs();
+#endif
+ return 2;
+ }
+
+ /* Copy to backup tree if a file. */
+ if (!ret) {
+ if (copy_file(fname, buf, -1, file->mode) < 0) {
+ rsyserr(FERROR, errno, "keep_backup failed: %s -> \"%s\"",
+ full_fname(fname), buf);
+ unmake_file(file);
+#ifdef SUPPORT_ACLS
+ uncache_tmp_acls();
+#endif
+#ifdef SUPPORT_XATTRS
+ uncache_tmp_xattrs();
+#endif
+ return 0;
+ }
+ if (DEBUG_GTE(BACKUP, 1))
+ rprintf(FINFO, "make_backup: COPY %s successful.\n", fname);
+ ret = 2;
+ }
+
+ save_preserve_xattrs = preserve_xattrs;
+ preserve_xattrs = 0;
+ set_file_attrs(buf, file, NULL, fname, 0);
+ preserve_xattrs = save_preserve_xattrs;
+
+ unmake_file(file);
+#ifdef SUPPORT_ACLS
+ uncache_tmp_acls();
+#endif
+#ifdef SUPPORT_XATTRS
+ uncache_tmp_xattrs();
+#endif
+
+ success:
+ if (INFO_GTE(BACKUP, 1))
+ rprintf(FINFO, "backed up %s to %s\n", fname, buf);
+ return ret;
+}
diff --git a/rsync/batch.c b/rsync/batch.c
new file mode 100644
index 0000000..5b329ba
--- /dev/null
+++ b/rsync/batch.c
@@ -0,0 +1,283 @@
+/*
+ * Support for the batch-file options.
+ *
+ * Copyright (C) 1999 Weiss
+ * Copyright (C) 2004 Chris Shoemaker
+ * Copyright (C) 2004-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include
+#include
+
+extern int eol_nulls;
+extern int recurse;
+extern int xfer_dirs;
+extern int preserve_links;
+extern int preserve_hard_links;
+extern int preserve_devices;
+extern int preserve_uid;
+extern int preserve_gid;
+extern int preserve_acls;
+extern int preserve_xattrs;
+extern int always_checksum;
+extern int do_compression;
+extern int inplace;
+extern int append_mode;
+extern int protocol_version;
+extern char *batch_name;
+#ifdef ICONV_OPTION
+extern char *iconv_opt;
+#endif
+
+extern filter_rule_list filter_list;
+
+int batch_stream_flags;
+
+static int tweaked_append;
+static int tweaked_append_verify;
+static int tweaked_iconv;
+
+static int *flag_ptr[] = {
+ &recurse, /* 0 */
+ &preserve_uid, /* 1 */
+ &preserve_gid, /* 2 */
+ &preserve_links, /* 3 */
+ &preserve_devices, /* 4 */
+ &preserve_hard_links, /* 5 */
+ &always_checksum, /* 6 */
+ &xfer_dirs, /* 7 (protocol 29) */
+ &do_compression, /* 8 (protocol 29) */
+ &tweaked_iconv, /* 9 (protocol 30) */
+ &preserve_acls, /* 10 (protocol 30) */
+ &preserve_xattrs, /* 11 (protocol 30) */
+ &inplace, /* 12 (protocol 30) */
+ &tweaked_append, /* 13 (protocol 30) */
+ &tweaked_append_verify, /* 14 (protocol 30) */
+ NULL
+};
+
+static char *flag_name[] = {
+ "--recurse (-r)",
+ "--owner (-o)",
+ "--group (-g)",
+ "--links (-l)",
+ "--devices (-D)",
+ "--hard-links (-H)",
+ "--checksum (-c)",
+ "--dirs (-d)",
+ "--compress (-z)",
+ "--iconv",
+ "--acls (-A)",
+ "--xattrs (-X)",
+ "--inplace",
+ "--append",
+ "--append-verify",
+ NULL
+};
+
+void write_stream_flags(int fd)
+{
+ int i, flags;
+
+ tweaked_append = append_mode == 1;
+ tweaked_append_verify = append_mode == 2;
+#ifdef ICONV_OPTION
+ tweaked_iconv = iconv_opt != NULL;
+#endif
+
+ /* Start the batch file with a bitmap of data-stream-affecting
+ * flags. */
+ for (i = 0, flags = 0; flag_ptr[i]; i++) {
+ if (*flag_ptr[i])
+ flags |= 1 << i;
+ }
+ write_int(fd, flags);
+}
+
+void read_stream_flags(int fd)
+{
+ batch_stream_flags = read_int(fd);
+}
+
+void check_batch_flags(void)
+{
+ int i;
+
+ if (protocol_version < 29)
+ flag_ptr[7] = NULL;
+ else if (protocol_version < 30)
+ flag_ptr[9] = NULL;
+ tweaked_append = append_mode == 1;
+ tweaked_append_verify = append_mode == 2;
+#ifdef ICONV_OPTION
+ tweaked_iconv = iconv_opt != NULL;
+#endif
+ for (i = 0; flag_ptr[i]; i++) {
+ int set = batch_stream_flags & (1 << i) ? 1 : 0;
+ if (*flag_ptr[i] != set) {
+ if (i == 9) {
+ rprintf(FERROR,
+ "%s specify the --iconv option to use this batch file.\n",
+ set ? "Please" : "Do not");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (INFO_GTE(MISC, 1)) {
+ rprintf(FINFO,
+ "%sing the %s option to match the batchfile.\n",
+ set ? "Sett" : "Clear", flag_name[i]);
+ }
+ *flag_ptr[i] = set;
+ }
+ }
+ if (protocol_version < 29) {
+ if (recurse)
+ xfer_dirs |= 1;
+ else if (xfer_dirs < 2)
+ xfer_dirs = 0;
+ }
+
+ if (tweaked_append)
+ append_mode = 1;
+ else if (tweaked_append_verify)
+ append_mode = 2;
+}
+
+static int write_arg(int fd, char *arg)
+{
+ char *x, *s;
+ int len, ret = 0;
+
+ if (*arg == '-' && (x = strchr(arg, '=')) != NULL) {
+ if (write(fd, arg, x - arg + 1) != x - arg + 1)
+ ret = -1;
+ arg += x - arg + 1;
+ }
+
+ if (strpbrk(arg, " \"'&;|[]()$#!*?^\\") != NULL) {
+ if (write(fd, "'", 1) != 1)
+ ret = -1;
+ for (s = arg; (x = strchr(s, '\'')) != NULL; s = x + 1) {
+ if (write(fd, s, x - s + 1) != x - s + 1
+ || write(fd, "'", 1) != 1)
+ ret = -1;
+ }
+ len = strlen(s);
+ if (write(fd, s, len) != len
+ || write(fd, "'", 1) != 1)
+ ret = -1;
+ return ret;
+ }
+
+ len = strlen(arg);
+ if (write(fd, arg, len) != len)
+ ret = -1;
+
+ return ret;
+}
+
+static void write_filter_rules(int fd)
+{
+ filter_rule *ent;
+
+ write_sbuf(fd, " <<'#E#'\n");
+ for (ent = filter_list.head; ent; ent = ent->next) {
+ unsigned int plen;
+ char *p = get_rule_prefix(ent, "- ", 0, &plen);
+ write_buf(fd, p, plen);
+ write_sbuf(fd, ent->pattern);
+ if (ent->rflags & FILTRULE_DIRECTORY)
+ write_byte(fd, '/');
+ write_byte(fd, eol_nulls ? 0 : '\n');
+ }
+ if (eol_nulls)
+ write_sbuf(fd, ";\n");
+ write_sbuf(fd, "#E#");
+}
+
+/* This routine tries to write out an equivalent --read-batch command
+ * given the user's --write-batch args. However, it doesn't really
+ * understand most of the options, so it uses some overly simple
+ * heuristics to munge the command line into something that will
+ * (hopefully) work. */
+void write_batch_shell_file(int argc, char *argv[], int file_arg_cnt)
+{
+ int fd, i, len, err = 0;
+ char *p, filename[MAXPATHLEN];
+
+ stringjoin(filename, sizeof filename,
+ batch_name, ".sh", NULL);
+ fd = do_open(filename, O_WRONLY | O_CREAT | O_TRUNC,
+ S_IRUSR | S_IWUSR | S_IXUSR);
+ if (fd < 0) {
+ rsyserr(FERROR, errno, "Batch file %s open error",
+ filename);
+ exit_cleanup(RERR_FILESELECT);
+ }
+
+ /* Write argvs info to BATCH.sh file */
+ if (write_arg(fd, argv[0]) < 0)
+ err = 1;
+ if (filter_list.head) {
+ if (protocol_version >= 29)
+ write_sbuf(fd, " --filter=._-");
+ else
+ write_sbuf(fd, " --exclude-from=-");
+ }
+ for (i = 1; i < argc - file_arg_cnt; i++) {
+ p = argv[i];
+ if (strncmp(p, "--files-from", 12) == 0
+ || strncmp(p, "--filter", 8) == 0
+ || strncmp(p, "--include", 9) == 0
+ || strncmp(p, "--exclude", 9) == 0) {
+ if (strchr(p, '=') == NULL)
+ i++;
+ continue;
+ }
+ if (strcmp(p, "-f") == 0) {
+ i++;
+ continue;
+ }
+ if (write(fd, " ", 1) != 1)
+ err = 1;
+ if (strncmp(p, "--write-batch", len = 13) == 0
+ || strncmp(p, "--only-write-batch", len = 18) == 0) {
+ if (write(fd, "--read-batch", 12) != 12)
+ err = 1;
+ if (p[len] == '=') {
+ if (write(fd, "=", 1) != 1
+ || write_arg(fd, p + len + 1) < 0)
+ err = 1;
+ }
+ } else {
+ if (write_arg(fd, p) < 0)
+ err = 1;
+ }
+ }
+ if (!(p = check_for_hostspec(argv[argc - 1], &p, &i)))
+ p = argv[argc - 1];
+ if (write(fd, " ${1:-", 6) != 6
+ || write_arg(fd, p) < 0)
+ err = 1;
+ write_byte(fd, '}');
+ if (filter_list.head)
+ write_filter_rules(fd);
+ if (write(fd, "\n", 1) != 1 || close(fd) < 0 || err) {
+ rsyserr(FERROR, errno, "Batch file %s write error",
+ filename);
+ exit_cleanup(RERR_FILEIO);
+ }
+}
diff --git a/rsync/byteorder.h b/rsync/byteorder.h
new file mode 100644
index 0000000..49c358b
--- /dev/null
+++ b/rsync/byteorder.h
@@ -0,0 +1,124 @@
+/*
+ * Simple byteorder handling.
+ *
+ * Copyright (C) 1992-1995 Andrew Tridgell
+ * Copyright (C) 2007-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#undef CAREFUL_ALIGNMENT
+#undef AVOID_BYTEORDER_INLINE
+
+/* We know that the x86 can handle misalignment and has the same
+ * byte order (LSB-first) as the 32-bit numbers we transmit. */
+#if defined __i386__ || defined __i486__ || defined __i586__ || defined __i686__ || __amd64
+#define CAREFUL_ALIGNMENT 0
+#endif
+
+#ifndef CAREFUL_ALIGNMENT
+#define CAREFUL_ALIGNMENT 1
+#endif
+
+#define CVAL(buf,pos) (((unsigned char *)(buf))[pos])
+#define UVAL(buf,pos) ((uint32)CVAL(buf,pos))
+
+#if CAREFUL_ALIGNMENT
+
+#define PVAL(buf,pos) (UVAL(buf,pos)|UVAL(buf,(pos)+1)<<8)
+#define IVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+2)<<16)
+#define IVAL64(buf,pos) (IVAL(buf,pos)|(int64)IVAL(buf,(pos)+4)<<32)
+#define SSVALX(buf,pos,val) (CVAL(buf,pos)=(val)&0xFF,CVAL(buf,pos+1)=(val)>>8)
+#define SIVALX(buf,pos,val) (SSVALX(buf,pos,val&0xFFFF),SSVALX(buf,pos+2,val>>16))
+#define SIVAL(buf,pos,val) SIVALX(buf,pos,(uint32)(val))
+#define SIVAL64(buf,pos,val) (SIVAL(buf,pos,val),SIVAL(buf,(pos)+4,(val)>>32))
+
+#define IVALu(buf,pos) IVAL(buf,pos)
+#define SIVALu(buf,pos,val) SIVAL(buf,pos,val)
+
+#else /* !CAREFUL_ALIGNMENT */
+
+/* This handles things for architectures like the 386 that can handle alignment errors.
+ * WARNING: This section is dependent on the length of an int32 (and thus a uint32)
+ * being correct (4 bytes)! Set CAREFUL_ALIGNMENT if it is not. */
+
+# ifdef AVOID_BYTEORDER_INLINE
+
+#define IVAL(buf,pos) (*(uint32 *)((char *)(buf) + (pos)))
+#define SIVAL(buf,pos,val) IVAL(buf,pos)=((uint32)(val))
+
+#define IVALu(buf,pos) IVAL(buf,pos)
+#define SIVALu(buf,pos,val) SIVAL(buf,pos,val)
+
+# else /* !AVOID_BYTEORDER_INLINE */
+
+static inline uint32
+IVALu(const uchar *buf, int pos)
+{
+ union {
+ const uchar *b;
+ const uint32 *num;
+ } u;
+ u.b = buf + pos;
+ return *u.num;
+}
+
+static inline void
+SIVALu(uchar *buf, int pos, uint32 val)
+{
+ union {
+ uchar *b;
+ uint32 *num;
+ } u;
+ u.b = buf + pos;
+ *u.num = val;
+}
+
+static inline uint32
+IVAL(const char *buf, int pos)
+{
+ return IVALu((uchar*)buf, pos);
+}
+
+static inline void
+SIVAL(char *buf, int pos, uint32 val)
+{
+ SIVALu((uchar*)buf, pos, val);
+}
+
+static inline int64
+IVAL64(const char *buf, int pos)
+{
+ union {
+ const char *b;
+ const int64 *num;
+ } u;
+ u.b = buf + pos;
+ return *u.num;
+}
+
+static inline void
+SIVAL64(char *buf, int pos, int64 val)
+{
+ union {
+ char *b;
+ int64 *num;
+ } u;
+ u.b = buf + pos;
+ *u.num = val;
+}
+
+# endif /* !AVOID_BYTEORDER_INLINE */
+
+#endif /* !CAREFUL_ALIGNMENT */
diff --git a/rsync/case_N.h b/rsync/case_N.h
new file mode 100644
index 0000000..72451a9
--- /dev/null
+++ b/rsync/case_N.h
@@ -0,0 +1,76 @@
+/*
+ * Allow an arbitrary sequence of case labels.
+ *
+ * Copyright (C) 2006-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+/* This is included multiple times, once for every segement in a switch statement.
+ * This produces the next "case N:" statement in sequence. */
+
+#if !defined CASE_N_STATE_0
+#define CASE_N_STATE_0
+ case 0:
+#elif !defined CASE_N_STATE_1
+#define CASE_N_STATE_1
+ case 1:
+#elif !defined CASE_N_STATE_2
+#define CASE_N_STATE_2
+ case 2:
+#elif !defined CASE_N_STATE_3
+#define CASE_N_STATE_3
+ case 3:
+#elif !defined CASE_N_STATE_4
+#define CASE_N_STATE_4
+ case 4:
+#elif !defined CASE_N_STATE_5
+#define CASE_N_STATE_5
+ case 5:
+#elif !defined CASE_N_STATE_6
+#define CASE_N_STATE_6
+ case 6:
+#elif !defined CASE_N_STATE_7
+#define CASE_N_STATE_7
+ case 7:
+#elif !defined CASE_N_STATE_8
+#define CASE_N_STATE_8
+ case 8:
+#elif !defined CASE_N_STATE_9
+#define CASE_N_STATE_9
+ case 9:
+#elif !defined CASE_N_STATE_10
+#define CASE_N_STATE_10
+ case 10:
+#elif !defined CASE_N_STATE_11
+#define CASE_N_STATE_11
+ case 11:
+#elif !defined CASE_N_STATE_12
+#define CASE_N_STATE_12
+ case 12:
+#elif !defined CASE_N_STATE_13
+#define CASE_N_STATE_13
+ case 13:
+#elif !defined CASE_N_STATE_14
+#define CASE_N_STATE_14
+ case 14:
+#elif !defined CASE_N_STATE_15
+#define CASE_N_STATE_15
+ case 15:
+#elif !defined CASE_N_STATE_16
+#define CASE_N_STATE_16
+ case 16:
+#else
+#error Need to add more case statements!
+#endif
diff --git a/rsync/checksum.c b/rsync/checksum.c
new file mode 100644
index 0000000..a1c2aa2
--- /dev/null
+++ b/rsync/checksum.c
@@ -0,0 +1,223 @@
+/*
+ * Routines to support checksumming of bytes.
+ *
+ * Copyright (C) 1996 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2004-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+extern int checksum_seed;
+extern int protocol_version;
+
+/*
+ a simple 32 bit checksum that can be upadted from either end
+ (inspired by Mark Adler's Adler-32 checksum)
+ */
+uint32 get_checksum1(char *buf1, int32 len)
+{
+ int32 i;
+ uint32 s1, s2;
+ schar *buf = (schar *)buf1;
+
+ s1 = s2 = 0;
+ for (i = 0; i < (len-4); i+=4) {
+ s2 += 4*(s1 + buf[i]) + 3*buf[i+1] + 2*buf[i+2] + buf[i+3] +
+ 10*CHAR_OFFSET;
+ s1 += (buf[i+0] + buf[i+1] + buf[i+2] + buf[i+3] + 4*CHAR_OFFSET);
+ }
+ for (; i < len; i++) {
+ s1 += (buf[i]+CHAR_OFFSET); s2 += s1;
+ }
+ return (s1 & 0xffff) + (s2 << 16);
+}
+
+
+void get_checksum2(char *buf, int32 len, char *sum)
+{
+ md_context m;
+
+ if (protocol_version >= 30) {
+ uchar seedbuf[4];
+ md5_begin(&m);
+ md5_update(&m, (uchar *)buf, len);
+ if (checksum_seed) {
+ SIVALu(seedbuf, 0, checksum_seed);
+ md5_update(&m, seedbuf, 4);
+ }
+ md5_result(&m, (uchar *)sum);
+ } else {
+ int32 i;
+ static char *buf1;
+ static int32 len1;
+
+ mdfour_begin(&m);
+
+ if (len > len1) {
+ if (buf1)
+ free(buf1);
+ buf1 = new_array(char, len+4);
+ len1 = len;
+ if (!buf1)
+ out_of_memory("get_checksum2");
+ }
+
+ memcpy(buf1, buf, len);
+ if (checksum_seed) {
+ SIVAL(buf1,len,checksum_seed);
+ len += 4;
+ }
+
+ for (i = 0; i + CSUM_CHUNK <= len; i += CSUM_CHUNK)
+ mdfour_update(&m, (uchar *)(buf1+i), CSUM_CHUNK);
+
+ /*
+ * Prior to version 27 an incorrect MD4 checksum was computed
+ * by failing to call mdfour_tail() for block sizes that
+ * are multiples of 64. This is fixed by calling mdfour_update()
+ * even when there are no more bytes.
+ */
+ if (len - i > 0 || protocol_version >= 27)
+ mdfour_update(&m, (uchar *)(buf1+i), len-i);
+
+ mdfour_result(&m, (uchar *)sum);
+ }
+}
+
+void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum)
+{
+ struct map_struct *buf;
+ OFF_T i, len = st_p->st_size;
+ md_context m;
+ int32 remainder;
+ int fd;
+
+ memset(sum, 0, MAX_DIGEST_LEN);
+
+ fd = do_open(fname, O_RDONLY, 0);
+ if (fd == -1)
+ return;
+
+ buf = map_file(fd, len, MAX_MAP_SIZE, CSUM_CHUNK);
+
+ if (protocol_version >= 30) {
+ md5_begin(&m);
+
+ for (i = 0; i + CSUM_CHUNK <= len; i += CSUM_CHUNK) {
+ md5_update(&m, (uchar *)map_ptr(buf, i, CSUM_CHUNK),
+ CSUM_CHUNK);
+ }
+
+ remainder = (int32)(len - i);
+ if (remainder > 0)
+ md5_update(&m, (uchar *)map_ptr(buf, i, remainder), remainder);
+
+ md5_result(&m, (uchar *)sum);
+ } else {
+ mdfour_begin(&m);
+
+ for (i = 0; i + CSUM_CHUNK <= len; i += CSUM_CHUNK) {
+ mdfour_update(&m, (uchar *)map_ptr(buf, i, CSUM_CHUNK),
+ CSUM_CHUNK);
+ }
+
+ /* Prior to version 27 an incorrect MD4 checksum was computed
+ * by failing to call mdfour_tail() for block sizes that
+ * are multiples of 64. This is fixed by calling mdfour_update()
+ * even when there are no more bytes. */
+ remainder = (int32)(len - i);
+ if (remainder > 0 || protocol_version >= 27)
+ mdfour_update(&m, (uchar *)map_ptr(buf, i, remainder), remainder);
+
+ mdfour_result(&m, (uchar *)sum);
+ }
+
+ close(fd);
+ unmap_file(buf);
+}
+
+static int32 sumresidue;
+static md_context md;
+
+void sum_init(int seed)
+{
+ char s[4];
+
+ if (protocol_version >= 30)
+ md5_begin(&md);
+ else {
+ mdfour_begin(&md);
+ sumresidue = 0;
+ SIVAL(s, 0, seed);
+ sum_update(s, 4);
+ }
+}
+
+/**
+ * Feed data into an MD4 accumulator, md. The results may be
+ * retrieved using sum_end(). md is used for different purposes at
+ * different points during execution.
+ *
+ * @todo Perhaps get rid of md and just pass in the address each time.
+ * Very slightly clearer and slower.
+ **/
+void sum_update(const char *p, int32 len)
+{
+ if (protocol_version >= 30) {
+ md5_update(&md, (uchar *)p, len);
+ return;
+ }
+
+ if (len + sumresidue < CSUM_CHUNK) {
+ memcpy(md.buffer + sumresidue, p, len);
+ sumresidue += len;
+ return;
+ }
+
+ if (sumresidue) {
+ int32 i = CSUM_CHUNK - sumresidue;
+ memcpy(md.buffer + sumresidue, p, i);
+ mdfour_update(&md, (uchar *)md.buffer, CSUM_CHUNK);
+ len -= i;
+ p += i;
+ }
+
+ while (len >= CSUM_CHUNK) {
+ mdfour_update(&md, (uchar *)p, CSUM_CHUNK);
+ len -= CSUM_CHUNK;
+ p += CSUM_CHUNK;
+ }
+
+ sumresidue = len;
+ if (sumresidue)
+ memcpy(md.buffer, p, sumresidue);
+}
+
+int sum_end(char *sum)
+{
+ if (protocol_version >= 30) {
+ md5_result(&md, (uchar *)sum);
+ return MD5_DIGEST_LEN;
+ }
+
+ if (sumresidue || protocol_version >= 27)
+ mdfour_update(&md, (uchar *)md.buffer, sumresidue);
+
+ mdfour_result(&md, (uchar *)sum);
+
+ return MD4_DIGEST_LEN;
+}
diff --git a/rsync/chmod.c b/rsync/chmod.c
new file mode 100644
index 0000000..1f73958
--- /dev/null
+++ b/rsync/chmod.c
@@ -0,0 +1,249 @@
+/*
+ * Implement the core of the --chmod option.
+ *
+ * Copyright (C) 2002 Scott Howard
+ * Copyright (C) 2005-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "itypes.h"
+
+extern mode_t orig_umask;
+
+#define FLAG_X_KEEP (1<<0)
+#define FLAG_DIRS_ONLY (1<<1)
+#define FLAG_FILES_ONLY (1<<2)
+
+struct chmod_mode_struct {
+ struct chmod_mode_struct *next;
+ int ModeAND, ModeOR;
+ char flags;
+};
+
+#define CHMOD_ADD 1
+#define CHMOD_SUB 2
+#define CHMOD_EQ 3
+#define CHMOD_SET 4
+
+#define STATE_ERROR 0
+#define STATE_1ST_HALF 1
+#define STATE_2ND_HALF 2
+#define STATE_OCTAL_NUM 3
+
+/* Parse a chmod-style argument, and break it down into one or more AND/OR
+ * pairs in a linked list. We return a pointer to new items on succcess
+ * (appending the items to the specified list), or NULL on error. */
+struct chmod_mode_struct *parse_chmod(const char *modestr,
+ struct chmod_mode_struct **root_mode_ptr)
+{
+ int state = STATE_1ST_HALF;
+ int where = 0, what = 0, op = 0, topbits = 0, topoct = 0, flags = 0;
+ struct chmod_mode_struct *first_mode = NULL, *curr_mode = NULL,
+ *prev_mode = NULL;
+
+ while (state != STATE_ERROR) {
+ if (!*modestr || *modestr == ',') {
+ int bits;
+
+ if (!op) {
+ state = STATE_ERROR;
+ break;
+ }
+ prev_mode = curr_mode;
+ curr_mode = new_array(struct chmod_mode_struct, 1);
+ if (prev_mode)
+ prev_mode->next = curr_mode;
+ else
+ first_mode = curr_mode;
+ curr_mode->next = NULL;
+
+ if (where)
+ bits = where * what;
+ else {
+ where = 0111;
+ bits = (where * what) & ~orig_umask;
+ }
+
+ switch (op) {
+ case CHMOD_ADD:
+ curr_mode->ModeAND = CHMOD_BITS;
+ curr_mode->ModeOR = bits + topoct;
+ break;
+ case CHMOD_SUB:
+ curr_mode->ModeAND = CHMOD_BITS - bits - topoct;
+ curr_mode->ModeOR = 0;
+ break;
+ case CHMOD_EQ:
+ curr_mode->ModeAND = CHMOD_BITS - (where * 7) - (topoct ? topbits : 0);
+ curr_mode->ModeOR = bits + topoct;
+ break;
+ case CHMOD_SET:
+ curr_mode->ModeAND = 0;
+ curr_mode->ModeOR = bits;
+ break;
+ }
+
+ curr_mode->flags = flags;
+
+ if (!*modestr)
+ break;
+ modestr++;
+
+ state = STATE_1ST_HALF;
+ where = what = op = topoct = topbits = flags = 0;
+ }
+
+ switch (state) {
+ case STATE_1ST_HALF:
+ switch (*modestr) {
+ case 'D':
+ if (flags & FLAG_FILES_ONLY)
+ state = STATE_ERROR;
+ flags |= FLAG_DIRS_ONLY;
+ break;
+ case 'F':
+ if (flags & FLAG_DIRS_ONLY)
+ state = STATE_ERROR;
+ flags |= FLAG_FILES_ONLY;
+ break;
+ case 'u':
+ where |= 0100;
+ topbits |= 04000;
+ break;
+ case 'g':
+ where |= 0010;
+ topbits |= 02000;
+ break;
+ case 'o':
+ where |= 0001;
+ break;
+ case 'a':
+ where |= 0111;
+ break;
+ case '+':
+ op = CHMOD_ADD;
+ state = STATE_2ND_HALF;
+ break;
+ case '-':
+ op = CHMOD_SUB;
+ state = STATE_2ND_HALF;
+ break;
+ case '=':
+ op = CHMOD_EQ;
+ state = STATE_2ND_HALF;
+ break;
+ default:
+ if (isDigit(modestr) && *modestr < '8' && !where) {
+ op = CHMOD_SET;
+ state = STATE_OCTAL_NUM;
+ where = 1;
+ what = *modestr - '0';
+ } else
+ state = STATE_ERROR;
+ break;
+ }
+ break;
+ case STATE_2ND_HALF:
+ switch (*modestr) {
+ case 'r':
+ what |= 4;
+ break;
+ case 'w':
+ what |= 2;
+ break;
+ case 'X':
+ flags |= FLAG_X_KEEP;
+ /* FALL THROUGH */
+ case 'x':
+ what |= 1;
+ break;
+ case 's':
+ if (topbits)
+ topoct |= topbits;
+ else
+ topoct = 04000;
+ break;
+ case 't':
+ topoct |= 01000;
+ break;
+ default:
+ state = STATE_ERROR;
+ break;
+ }
+ break;
+ case STATE_OCTAL_NUM:
+ if (isDigit(modestr) && *modestr < '8') {
+ what = what*8 + *modestr - '0';
+ if (what > CHMOD_BITS)
+ state = STATE_ERROR;
+ } else
+ state = STATE_ERROR;
+ break;
+ }
+ modestr++;
+ }
+
+ if (state == STATE_ERROR) {
+ free_chmod_mode(first_mode);
+ return NULL;
+ }
+
+ if (!(curr_mode = *root_mode_ptr))
+ *root_mode_ptr = first_mode;
+ else {
+ while (curr_mode->next)
+ curr_mode = curr_mode->next;
+ curr_mode->next = first_mode;
+ }
+
+ return first_mode;
+}
+
+
+/* Takes an existing file permission and a list of AND/OR changes, and
+ * create a new permissions. */
+int tweak_mode(int mode, struct chmod_mode_struct *chmod_modes)
+{
+ int IsX = mode & 0111;
+ int NonPerm = mode & ~CHMOD_BITS;
+
+ for ( ; chmod_modes; chmod_modes = chmod_modes->next) {
+ if ((chmod_modes->flags & FLAG_DIRS_ONLY) && !S_ISDIR(NonPerm))
+ continue;
+ if ((chmod_modes->flags & FLAG_FILES_ONLY) && S_ISDIR(NonPerm))
+ continue;
+ mode &= chmod_modes->ModeAND;
+ if ((chmod_modes->flags & FLAG_X_KEEP) && !IsX && !S_ISDIR(NonPerm))
+ mode |= chmod_modes->ModeOR & ~0111;
+ else
+ mode |= chmod_modes->ModeOR;
+ }
+
+ return mode | NonPerm;
+}
+
+/* Free the linked list created by parse_chmod. */
+int free_chmod_mode(struct chmod_mode_struct *chmod_modes)
+{
+ struct chmod_mode_struct *next;
+
+ while (chmod_modes) {
+ next = chmod_modes->next;
+ free(chmod_modes);
+ chmod_modes = next;
+ }
+ return 0;
+}
diff --git a/rsync/cleanup.c b/rsync/cleanup.c
new file mode 100644
index 0000000..cd023aa
--- /dev/null
+++ b/rsync/cleanup.c
@@ -0,0 +1,292 @@
+/*
+ * End-of-run cleanup routines.
+ *
+ * Copyright (C) 1996-2000 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2002 Martin Pool
+ * Copyright (C) 2003-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+extern int am_server;
+extern int am_daemon;
+extern int am_receiver;
+extern int io_error;
+extern int keep_partial;
+extern int got_xfer_error;
+extern int protocol_version;
+extern int output_needs_newline;
+extern char *partial_dir;
+extern char *logfile_name;
+
+BOOL shutting_down = False;
+BOOL flush_ok_after_signal = False;
+
+#ifdef HAVE_SIGACTION
+static struct sigaction sigact;
+#endif
+
+/**
+ * Close all open sockets and files, allowing a (somewhat) graceful
+ * shutdown() of socket connections. This eliminates the abortive
+ * TCP RST sent by a Winsock-based system when the close() occurs.
+ **/
+void close_all(void)
+{
+#ifdef SHUTDOWN_ALL_SOCKETS
+ int max_fd;
+ int fd;
+ int ret;
+ STRUCT_STAT st;
+
+ max_fd = sysconf(_SC_OPEN_MAX) - 1;
+ for (fd = max_fd; fd >= 0; fd--) {
+ if ((ret = do_fstat(fd, &st)) == 0) {
+ if (is_a_socket(fd))
+ ret = shutdown(fd, 2);
+ ret = close(fd);
+ }
+ }
+#endif
+}
+
+/**
+ * @file cleanup.c
+ *
+ * Code for handling interrupted transfers. Depending on the @c
+ * --partial option, we may either delete the temporary file, or go
+ * ahead and overwrite the destination. This second behaviour only
+ * occurs if we've sent literal data and therefore hopefully made
+ * progress on the transfer.
+ **/
+
+/**
+ * Set to True once literal data has been sent across the link for the
+ * current file. (????)
+ *
+ * Handling the cleanup when a transfer is interrupted is tricky when
+ * --partial is selected. We need to ensure that the partial file is
+ * kept if any real data has been transferred.
+ **/
+int cleanup_got_literal = 0;
+
+static const char *cleanup_fname;
+static const char *cleanup_new_fname;
+static struct file_struct *cleanup_file;
+static int cleanup_fd_r = -1, cleanup_fd_w = -1;
+static pid_t cleanup_pid = 0;
+
+pid_t cleanup_child_pid = -1;
+
+/**
+ * Eventually calls exit(), passing @p code, therefore does not return.
+ *
+ * @param code one of the RERR_* codes from errcode.h.
+ **/
+NORETURN void _exit_cleanup(int code, const char *file, int line)
+{
+ static int switch_step = 0;
+ static int exit_code = 0, exit_line = 0;
+ static const char *exit_file = NULL;
+ static int first_code = 0;
+
+ SIGACTION(SIGUSR1, SIG_IGN);
+ SIGACTION(SIGUSR2, SIG_IGN);
+
+ if (!exit_code) { /* Preserve first error exit info when recursing. */
+ exit_code = code;
+ exit_file = file;
+ exit_line = line < 0 ? -line : line;
+ }
+
+ /* If this is the exit at the end of the run, the server side
+ * should not attempt to output a message (see log_exit()). */
+ if (am_server && code == 0)
+ am_server = 2;
+
+ /* Some of our actions might cause a recursive call back here, so we
+ * keep track of where we are in the cleanup and never repeat a step. */
+ switch (switch_step) {
+#include "case_N.h" /* case 0: */
+ switch_step++;
+
+ first_code = code;
+
+ if (output_needs_newline) {
+ fputc('\n', stdout);
+ output_needs_newline = 0;
+ }
+
+ if (DEBUG_GTE(EXIT, 2)) {
+ rprintf(FINFO,
+ "[%s] _exit_cleanup(code=%d, file=%s, line=%d): entered\n",
+ who_am_i(), code, file, line);
+ }
+
+ /* FALLTHROUGH */
+#include "case_N.h"
+ switch_step++;
+
+ if (cleanup_child_pid != -1) {
+ int status;
+ int pid = wait_process(cleanup_child_pid, &status, WNOHANG);
+ if (pid == cleanup_child_pid) {
+ status = WEXITSTATUS(status);
+ if (status > exit_code)
+ exit_code = status;
+ }
+ }
+
+ /* FALLTHROUGH */
+#include "case_N.h"
+ switch_step++;
+
+ if (cleanup_got_literal && (cleanup_fname || cleanup_fd_w != -1)) {
+ if (cleanup_fd_r != -1) {
+ close(cleanup_fd_r);
+ cleanup_fd_r = -1;
+ }
+ if (cleanup_fd_w != -1) {
+ flush_write_file(cleanup_fd_w);
+ close(cleanup_fd_w);
+ cleanup_fd_w = -1;
+ }
+ if (cleanup_fname && cleanup_new_fname && keep_partial
+ && handle_partial_dir(cleanup_new_fname, PDIR_CREATE)) {
+ int tweak_modtime = 0;
+ const char *fname = cleanup_fname;
+ cleanup_fname = NULL;
+ if (!partial_dir) {
+ /* We don't want to leave a partial file with a modern time or it
+ * could be skipped via --update. Setting the time to something
+ * really old also helps it to stand out as unfinished in an ls. */
+ tweak_modtime = 1;
+ cleanup_file->modtime = 0;
+ }
+ finish_transfer(cleanup_new_fname, fname, NULL, NULL,
+ cleanup_file, tweak_modtime, !partial_dir);
+ }
+ }
+
+ /* FALLTHROUGH */
+#include "case_N.h"
+ switch_step++;
+
+ if (flush_ok_after_signal) {
+ flush_ok_after_signal = False;
+ if (code == RERR_SIGNAL)
+ io_flush(FULL_FLUSH);
+ }
+ if (!exit_code && !code)
+ io_flush(FULL_FLUSH);
+
+ /* FALLTHROUGH */
+#include "case_N.h"
+ switch_step++;
+
+ if (cleanup_fname)
+ do_unlink(cleanup_fname);
+ if (exit_code)
+ kill_all(SIGUSR1);
+ if (cleanup_pid && cleanup_pid == getpid()) {
+ char *pidf = lp_pid_file();
+ if (pidf && *pidf)
+ unlink(lp_pid_file());
+ }
+
+ if (exit_code == 0) {
+ if (code)
+ exit_code = code;
+ if (io_error & IOERR_DEL_LIMIT)
+ exit_code = RERR_DEL_LIMIT;
+ if (io_error & IOERR_VANISHED)
+ exit_code = RERR_VANISHED;
+ if (io_error & IOERR_GENERAL || got_xfer_error)
+ exit_code = RERR_PARTIAL;
+ }
+
+ /* If line < 0, this exit is after a MSG_ERROR_EXIT event, so
+ * we don't want to output a duplicate error. */
+ if ((exit_code && line > 0)
+ || am_daemon || (logfile_name && (am_server || !INFO_GTE(STATS, 1))))
+ log_exit(exit_code, exit_file, exit_line);
+
+ /* FALLTHROUGH */
+#include "case_N.h"
+ switch_step++;
+
+ if (DEBUG_GTE(EXIT, 1)) {
+ rprintf(FINFO,
+ "[%s] _exit_cleanup(code=%d, file=%s, line=%d): "
+ "about to call exit(%d)\n",
+ who_am_i(), first_code, exit_file, exit_line, exit_code);
+ }
+
+ /* FALLTHROUGH */
+#include "case_N.h"
+ switch_step++;
+
+ if (exit_code && exit_code != RERR_SOCKETIO && exit_code != RERR_STREAMIO && exit_code != RERR_SIGNAL1
+ && exit_code != RERR_TIMEOUT && !shutting_down && (protocol_version >= 31 || am_receiver)) {
+ if (line > 0) {
+ if (DEBUG_GTE(EXIT, 3)) {
+ rprintf(FINFO, "[%s] sending MSG_ERROR_EXIT with exit_code %d\n",
+ who_am_i(), exit_code);
+ }
+ send_msg_int(MSG_ERROR_EXIT, exit_code);
+ }
+ noop_io_until_death();
+ }
+
+ /* FALLTHROUGH */
+#include "case_N.h"
+ switch_step++;
+
+ if (am_server && exit_code)
+ msleep(100);
+ close_all();
+
+ /* FALLTHROUGH */
+ default:
+ break;
+ }
+
+ exit(exit_code);
+}
+
+void cleanup_disable(void)
+{
+ cleanup_fname = cleanup_new_fname = NULL;
+ cleanup_fd_r = cleanup_fd_w = -1;
+ cleanup_got_literal = 0;
+}
+
+
+void cleanup_set(const char *fnametmp, const char *fname, struct file_struct *file,
+ int fd_r, int fd_w)
+{
+ cleanup_fname = fnametmp;
+ cleanup_new_fname = fname; /* can be NULL on a partial-dir failure */
+ cleanup_file = file;
+ cleanup_fd_r = fd_r;
+ cleanup_fd_w = fd_w;
+}
+
+void cleanup_set_pid(pid_t pid)
+{
+ cleanup_pid = pid;
+}
diff --git a/rsync/clientname.c b/rsync/clientname.c
new file mode 100644
index 0000000..ccd43d7
--- /dev/null
+++ b/rsync/clientname.c
@@ -0,0 +1,348 @@
+/*
+ * Functions for looking up the remote name or addr of a socket.
+ *
+ * Copyright (C) 1992-2001 Andrew Tridgell
+ * Copyright (C) 2001, 2002 Martin Pool
+ * Copyright (C) 2002-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+/*
+ * This file is now converted to use the new-style getaddrinfo()
+ * interface, which supports IPv6 but is also supported on recent
+ * IPv4-only machines. On systems that don't have that interface, we
+ * emulate it using the KAME implementation.
+ */
+
+#include "rsync.h"
+
+static const char default_name[] = "UNKNOWN";
+extern int am_server;
+
+
+/**
+ * Return the IP addr of the client as a string
+ **/
+char *client_addr(int fd)
+{
+ static char addr_buf[100];
+ static int initialised;
+ struct sockaddr_storage ss;
+ socklen_t length = sizeof ss;
+
+ if (initialised)
+ return addr_buf;
+
+ initialised = 1;
+
+ if (am_server) { /* daemon over --rsh mode */
+ char *env_str;
+ strlcpy(addr_buf, "0.0.0.0", sizeof addr_buf);
+ if ((env_str = getenv("REMOTE_HOST")) != NULL
+ || (env_str = getenv("SSH_CONNECTION")) != NULL
+ || (env_str = getenv("SSH_CLIENT")) != NULL
+ || (env_str = getenv("SSH2_CLIENT")) != NULL) {
+ char *p;
+ strlcpy(addr_buf, env_str, sizeof addr_buf);
+ /* Truncate the value to just the IP address. */
+ if ((p = strchr(addr_buf, ' ')) != NULL)
+ *p = '\0';
+ }
+ } else {
+ client_sockaddr(fd, &ss, &length);
+ getnameinfo((struct sockaddr *)&ss, length,
+ addr_buf, sizeof addr_buf, NULL, 0, NI_NUMERICHOST);
+ }
+
+ return addr_buf;
+}
+
+
+static int get_sockaddr_family(const struct sockaddr_storage *ss)
+{
+ return ((struct sockaddr *) ss)->sa_family;
+}
+
+
+/**
+ * Return the DNS name of the client.
+ *
+ * The name is statically cached so that repeated lookups are quick,
+ * so there is a limit of one lookup per customer.
+ *
+ * If anything goes wrong, including the name->addr->name check, then
+ * we just use "UNKNOWN", so you can use that value in hosts allow
+ * lines.
+ *
+ * After translation from sockaddr to name we do a forward lookup to
+ * make sure nobody is spoofing PTR records.
+ **/
+char *client_name(int fd)
+{
+ static char name_buf[100];
+ static char port_buf[100];
+ static int initialised;
+ struct sockaddr_storage ss;
+ socklen_t ss_len;
+
+ if (initialised)
+ return name_buf;
+
+ strlcpy(name_buf, default_name, sizeof name_buf);
+ initialised = 1;
+
+ memset(&ss, 0, sizeof ss);
+
+ if (am_server) { /* daemon over --rsh mode */
+ char *addr = client_addr(fd);
+ struct addrinfo hint, *answer;
+ int err;
+
+ if (strcmp(addr, "0.0.0.0") == 0)
+ return name_buf;
+
+ memset(&hint, 0, sizeof hint);
+
+#ifdef AI_NUMERICHOST
+ hint.ai_flags = AI_NUMERICHOST;
+#endif
+ hint.ai_socktype = SOCK_STREAM;
+
+ if ((err = getaddrinfo(addr, NULL, &hint, &answer)) != 0) {
+ rprintf(FLOG, "malformed address %s: %s\n",
+ addr, gai_strerror(err));
+ return name_buf;
+ }
+
+ switch (answer->ai_family) {
+ case AF_INET:
+ ss_len = sizeof (struct sockaddr_in);
+ memcpy(&ss, answer->ai_addr, ss_len);
+ break;
+#ifdef INET6
+ case AF_INET6:
+ ss_len = sizeof (struct sockaddr_in6);
+ memcpy(&ss, answer->ai_addr, ss_len);
+ break;
+#endif
+ default:
+ exit_cleanup(RERR_SOCKETIO);
+ }
+ freeaddrinfo(answer);
+ } else {
+ ss_len = sizeof ss;
+ client_sockaddr(fd, &ss, &ss_len);
+ }
+
+ if (lookup_name(fd, &ss, ss_len, name_buf, sizeof name_buf,
+ port_buf, sizeof port_buf) == 0)
+ check_name(fd, &ss, name_buf, sizeof name_buf);
+
+ return name_buf;
+}
+
+
+
+/**
+ * Get the sockaddr for the client.
+ *
+ * If it comes in as an ipv4 address mapped into IPv6 format then we
+ * convert it back to a regular IPv4.
+ **/
+void client_sockaddr(int fd,
+ struct sockaddr_storage *ss,
+ socklen_t *ss_len)
+{
+ memset(ss, 0, sizeof *ss);
+
+ if (getpeername(fd, (struct sockaddr *) ss, ss_len)) {
+ /* FIXME: Can we really not continue? */
+ rsyserr(FLOG, errno, "getpeername on fd%d failed", fd);
+ exit_cleanup(RERR_SOCKETIO);
+ }
+
+#ifdef INET6
+ if (get_sockaddr_family(ss) == AF_INET6 &&
+ IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)ss)->sin6_addr)) {
+ /* OK, so ss is in the IPv6 family, but it is really
+ * an IPv4 address: something like
+ * "::ffff:10.130.1.2". If we use it as-is, then the
+ * reverse lookup might fail or perhaps something else
+ * bad might happen. So instead we convert it to an
+ * equivalent address in the IPv4 address family. */
+ struct sockaddr_in6 sin6;
+ struct sockaddr_in *sin;
+
+ memcpy(&sin6, ss, sizeof sin6);
+ sin = (struct sockaddr_in *)ss;
+ memset(sin, 0, sizeof *sin);
+ sin->sin_family = AF_INET;
+ *ss_len = sizeof (struct sockaddr_in);
+#ifdef HAVE_SOCKADDR_IN_LEN
+ sin->sin_len = *ss_len;
+#endif
+ sin->sin_port = sin6.sin6_port;
+
+ /* There is a macro to extract the mapped part
+ * (IN6_V4MAPPED_TO_SINADDR ?), but it does not seem
+ * to be present in the Linux headers. */
+ memcpy(&sin->sin_addr, &sin6.sin6_addr.s6_addr[12],
+ sizeof sin->sin_addr);
+ }
+#endif
+}
+
+
+/**
+ * Look up a name from @p ss into @p name_buf.
+ *
+ * @param fd file descriptor for client socket.
+ **/
+int lookup_name(int fd, const struct sockaddr_storage *ss,
+ socklen_t ss_len,
+ char *name_buf, size_t name_buf_size,
+ char *port_buf, size_t port_buf_size)
+{
+ int name_err;
+
+ /* reverse lookup */
+ name_err = getnameinfo((struct sockaddr *) ss, ss_len,
+ name_buf, name_buf_size,
+ port_buf, port_buf_size,
+ NI_NAMEREQD | NI_NUMERICSERV);
+ if (name_err != 0) {
+ strlcpy(name_buf, default_name, name_buf_size);
+ rprintf(FLOG, "name lookup failed for %s: %s\n",
+ client_addr(fd), gai_strerror(name_err));
+ return name_err;
+ }
+
+ return 0;
+}
+
+
+
+/**
+ * Compare an addrinfo from the resolver to a sockinfo.
+ *
+ * Like strcmp, returns 0 for identical.
+ **/
+int compare_addrinfo_sockaddr(const struct addrinfo *ai,
+ const struct sockaddr_storage *ss)
+{
+ int ss_family = get_sockaddr_family(ss);
+ const char fn[] = "compare_addrinfo_sockaddr";
+
+ if (ai->ai_family != ss_family) {
+ rprintf(FLOG, "%s: response family %d != %d\n",
+ fn, ai->ai_family, ss_family);
+ return 1;
+ }
+
+ /* The comparison method depends on the particular AF. */
+ if (ss_family == AF_INET) {
+ const struct sockaddr_in *sin1, *sin2;
+
+ sin1 = (const struct sockaddr_in *) ss;
+ sin2 = (const struct sockaddr_in *) ai->ai_addr;
+
+ return memcmp(&sin1->sin_addr, &sin2->sin_addr,
+ sizeof sin1->sin_addr);
+ }
+
+#ifdef INET6
+ if (ss_family == AF_INET6) {
+ const struct sockaddr_in6 *sin1, *sin2;
+
+ sin1 = (const struct sockaddr_in6 *) ss;
+ sin2 = (const struct sockaddr_in6 *) ai->ai_addr;
+
+ if (ai->ai_addrlen < sizeof (struct sockaddr_in6)) {
+ rprintf(FLOG, "%s: too short sockaddr_in6; length=%d\n",
+ fn, (int)ai->ai_addrlen);
+ return 1;
+ }
+
+ if (memcmp(&sin1->sin6_addr, &sin2->sin6_addr,
+ sizeof sin1->sin6_addr))
+ return 1;
+
+#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
+ if (sin1->sin6_scope_id != sin2->sin6_scope_id)
+ return 1;
+#endif
+ return 0;
+ }
+#endif /* INET6 */
+
+ /* don't know */
+ return 1;
+}
+
+
+/**
+ * Do a forward lookup on @p name_buf and make sure it corresponds to
+ * @p ss -- otherwise we may be being spoofed. If we suspect we are,
+ * then we don't abort the connection but just emit a warning, and
+ * change @p name_buf to be "UNKNOWN".
+ *
+ * We don't do anything with the service when checking the name,
+ * because it doesn't seem that it could be spoofed in any way, and
+ * getaddrinfo on random service names seems to cause problems on AIX.
+ **/
+int check_name(int fd,
+ const struct sockaddr_storage *ss,
+ char *name_buf, size_t name_buf_size)
+{
+ struct addrinfo hints, *res, *res0;
+ int error;
+ int ss_family = get_sockaddr_family(ss);
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = ss_family;
+ hints.ai_flags = AI_CANONNAME;
+ hints.ai_socktype = SOCK_STREAM;
+ error = getaddrinfo(name_buf, NULL, &hints, &res0);
+ if (error) {
+ rprintf(FLOG, "forward name lookup for %s failed: %s\n",
+ name_buf, gai_strerror(error));
+ strlcpy(name_buf, default_name, name_buf_size);
+ return error;
+ }
+
+ /* Given all these results, we expect that one of them will be
+ * the same as ss. The comparison is a bit complicated. */
+ for (res = res0; res; res = res->ai_next) {
+ if (!compare_addrinfo_sockaddr(res, ss))
+ break; /* OK, identical */
+ }
+
+ if (!res0) {
+ /* We hit the end of the list without finding an
+ * address that was the same as ss. */
+ rprintf(FLOG, "no known address for \"%s\": "
+ "spoofed address?\n", name_buf);
+ strlcpy(name_buf, default_name, name_buf_size);
+ } else if (res == NULL) {
+ /* We hit the end of the list without finding an
+ * address that was the same as ss. */
+ rprintf(FLOG, "%s is not a known address for \"%s\": "
+ "spoofed address?\n", client_addr(fd), name_buf);
+ strlcpy(name_buf, default_name, name_buf_size);
+ }
+
+ freeaddrinfo(res0);
+ return 0;
+}
diff --git a/rsync/clientserver.c b/rsync/clientserver.c
new file mode 100644
index 0000000..d9b5bb0
--- /dev/null
+++ b/rsync/clientserver.c
@@ -0,0 +1,1204 @@
+/*
+ * The socket based protocol for setting up a connection with rsyncd.
+ *
+ * Copyright (C) 1998-2001 Andrew Tridgell
+ * Copyright (C) 2001-2002 Martin Pool
+ * Copyright (C) 2002-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "itypes.h"
+
+extern int quiet;
+extern int dry_run;
+extern int output_motd;
+extern int list_only;
+extern int am_sender;
+extern int am_server;
+extern int am_daemon;
+extern int am_root;
+extern int rsync_port;
+extern int protect_args;
+extern int ignore_errors;
+extern int preserve_xattrs;
+extern int kluge_around_eof;
+extern int daemon_over_rsh;
+extern int munge_symlinks;
+extern int sanitize_paths;
+extern int numeric_ids;
+extern int filesfrom_fd;
+extern int remote_protocol;
+extern int protocol_version;
+extern int io_timeout;
+extern int no_detach;
+extern int write_batch;
+extern int default_af_hint;
+extern int logfile_format_has_i;
+extern int logfile_format_has_o_or_i;
+extern char *bind_address;
+extern char *config_file;
+extern char *logfile_format;
+extern char *files_from;
+extern char *tmpdir;
+extern struct chmod_mode_struct *chmod_modes;
+extern filter_rule_list daemon_filter_list;
+#ifdef ICONV_OPTION
+extern char *iconv_opt;
+extern iconv_t ic_send, ic_recv;
+#endif
+
+#define MAX_GID_LIST 32
+
+char *auth_user;
+int read_only = 0;
+int module_id = -1;
+struct chmod_mode_struct *daemon_chmod_modes;
+
+/* module_dirlen is the length of the module_dir string when in daemon
+ * mode and module_dir is not "/"; otherwise 0. (Note that a chroot-
+ * enabled module can have a non-"/" module_dir these days.) */
+char *module_dir = NULL;
+unsigned int module_dirlen = 0;
+
+char *full_module_path;
+
+static int rl_nulls = 0;
+
+#ifdef HAVE_SIGACTION
+static struct sigaction sigact;
+#endif
+
+static gid_t gid_list[MAX_GID_LIST];
+static int gid_count = 0;
+
+/* Used when "reverse lookup" is off. */
+const char undetermined_hostname[] = "UNDETERMINED";
+
+/**
+ * Run a client connected to an rsyncd. The alternative to this
+ * function for remote-shell connections is do_cmd().
+ *
+ * After negotiating which module to use and reading the server's
+ * motd, this hands over to client_run(). Telling the server the
+ * module will cause it to chroot/setuid/etc.
+ *
+ * Instead of doing a transfer, the client may at this stage instead
+ * get a listing of remote modules and exit.
+ *
+ * @return -1 for error in startup, or the result of client_run().
+ * Either way, it eventually gets passed to exit_cleanup().
+ **/
+int start_socket_client(char *host, int remote_argc, char *remote_argv[],
+ int argc, char *argv[])
+{
+ int fd, ret;
+ char *p, *user = NULL;
+
+ /* This is redundant with code in start_inband_exchange(), but this
+ * short-circuits a problem in the client before we open a socket,
+ * and the extra check won't hurt. */
+ if (**remote_argv == '/') {
+ rprintf(FERROR,
+ "ERROR: The remote path must start with a module name not a /\n");
+ return -1;
+ }
+
+ if ((p = strrchr(host, '@')) != NULL) {
+ user = host;
+ host = p+1;
+ *p = '\0';
+ }
+
+ fd = open_socket_out_wrapped(host, rsync_port, bind_address,
+ default_af_hint);
+ if (fd == -1)
+ exit_cleanup(RERR_SOCKETIO);
+
+#ifdef ICONV_CONST
+ setup_iconv();
+#endif
+
+ ret = start_inband_exchange(fd, fd, user, remote_argc, remote_argv);
+
+ return ret ? ret : client_run(fd, fd, -1, argc, argv);
+}
+
+static int exchange_protocols(int f_in, int f_out, char *buf, size_t bufsiz, int am_client)
+{
+ int remote_sub = -1;
+#if SUBPROTOCOL_VERSION != 0
+ int our_sub = protocol_version < PROTOCOL_VERSION ? 0 : SUBPROTOCOL_VERSION;
+#else
+ int our_sub = 0;
+#endif
+ char *motd;
+
+ io_printf(f_out, "@RSYNCD: %d.%d\n", protocol_version, our_sub);
+
+ if (!am_client) {
+ motd = lp_motd_file();
+ if (motd && *motd) {
+ FILE *f = fopen(motd,"r");
+ while (f && !feof(f)) {
+ int len = fread(buf, 1, bufsiz - 1, f);
+ if (len > 0)
+ write_buf(f_out, buf, len);
+ }
+ if (f)
+ fclose(f);
+ write_sbuf(f_out, "\n");
+ }
+ }
+
+ /* This strips the \n. */
+ if (!read_line_old(f_in, buf, bufsiz, 0)) {
+ if (am_client)
+ rprintf(FERROR, "rsync: did not see server greeting\n");
+ return -1;
+ }
+
+ if (sscanf(buf, "@RSYNCD: %d.%d", &remote_protocol, &remote_sub) < 1) {
+ if (am_client)
+ rprintf(FERROR, "rsync: server sent \"%s\" rather than greeting\n", buf);
+ else
+ io_printf(f_out, "@ERROR: protocol startup error\n");
+ return -1;
+ }
+
+ if (remote_sub < 0) {
+ if (remote_protocol == 30) {
+ if (am_client)
+ rprintf(FERROR, "rsync: server is speaking an incompatible beta of protocol 30\n");
+ else
+ io_printf(f_out, "@ERROR: your client is speaking an incompatible beta of protocol 30\n");
+ return -1;
+ }
+ remote_sub = 0;
+ }
+
+ if (protocol_version > remote_protocol) {
+ protocol_version = remote_protocol;
+ if (remote_sub)
+ protocol_version--;
+ } else if (protocol_version == remote_protocol) {
+ if (remote_sub != our_sub)
+ protocol_version--;
+ }
+#if SUBPROTOCOL_VERSION != 0
+ else if (protocol_version < remote_protocol) {
+ if (our_sub)
+ protocol_version--;
+ }
+#endif
+
+ if (protocol_version >= 30)
+ rl_nulls = 1;
+
+ return 0;
+}
+
+int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char *argv[])
+{
+ int i, modlen;
+ char line[BIGPATHBUFLEN];
+ char *sargs[MAX_ARGS];
+ int sargc = 0;
+ char *p, *modname;
+
+ assert(argc > 0 && *argv != NULL);
+
+ if (**argv == '/') {
+ rprintf(FERROR,
+ "ERROR: The remote path must start with a module name\n");
+ return -1;
+ }
+
+ if (!(p = strchr(*argv, '/')))
+ modlen = strlen(*argv);
+ else
+ modlen = p - *argv;
+
+ if (!(modname = new_array(char, modlen+1+1))) /* room for '/' & '\0' */
+ out_of_memory("start_inband_exchange");
+ strlcpy(modname, *argv, modlen + 1);
+ modname[modlen] = '/';
+ modname[modlen+1] = '\0';
+
+ if (!user)
+ user = getenv("USER");
+ if (!user)
+ user = getenv("LOGNAME");
+
+ if (exchange_protocols(f_in, f_out, line, sizeof line, 1) < 0)
+ return -1;
+
+ /* set daemon_over_rsh to false since we need to build the
+ * true set of args passed through the rsh/ssh connection;
+ * this is a no-op for direct-socket-connection mode */
+ daemon_over_rsh = 0;
+ server_options(sargs, &sargc);
+
+ if (sargc >= MAX_ARGS - 2)
+ goto arg_overflow;
+
+ sargs[sargc++] = ".";
+
+ while (argc > 0) {
+ if (sargc >= MAX_ARGS - 1) {
+ arg_overflow:
+ rprintf(FERROR, "internal: args[] overflowed in do_cmd()\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (strncmp(*argv, modname, modlen) == 0
+ && argv[0][modlen] == '\0')
+ sargs[sargc++] = modname; /* we send "modname/" */
+ else if (**argv == '-') {
+ if (asprintf(sargs + sargc++, "./%s", *argv) < 0)
+ out_of_memory("start_inband_exchange");
+ } else
+ sargs[sargc++] = *argv;
+ argv++;
+ argc--;
+ }
+
+ sargs[sargc] = NULL;
+
+ if (DEBUG_GTE(CMD, 1))
+ print_child_argv("sending daemon args:", sargs);
+
+ io_printf(f_out, "%.*s\n", modlen, modname);
+
+ /* Old servers may just drop the connection here,
+ rather than sending a proper EXIT command. Yuck. */
+ kluge_around_eof = list_only && protocol_version < 25 ? 1 : 0;
+
+ while (1) {
+ if (!read_line_old(f_in, line, sizeof line, 0)) {
+ rprintf(FERROR, "rsync: didn't get server startup line\n");
+ return -1;
+ }
+
+ if (strncmp(line,"@RSYNCD: AUTHREQD ",18) == 0) {
+ auth_client(f_out, user, line+18);
+ continue;
+ }
+
+ if (strcmp(line,"@RSYNCD: OK") == 0)
+ break;
+
+ if (strcmp(line,"@RSYNCD: EXIT") == 0) {
+ /* This is sent by recent versions of the
+ * server to terminate the listing of modules.
+ * We don't want to go on and transfer
+ * anything; just exit. */
+ exit(0);
+ }
+
+ if (strncmp(line, "@ERROR", 6) == 0) {
+ rprintf(FERROR, "%s\n", line);
+ /* This is always fatal; the server will now
+ * close the socket. */
+ return -1;
+ }
+
+ /* This might be a MOTD line or a module listing, but there is
+ * no way to differentiate it. The manpage mentions this. */
+ if (output_motd)
+ rprintf(FINFO, "%s\n", line);
+ }
+ kluge_around_eof = 0;
+
+ if (rl_nulls) {
+ for (i = 0; i < sargc; i++) {
+ if (!sargs[i]) /* stop at --protect-args NULL */
+ break;
+ write_sbuf(f_out, sargs[i]);
+ write_byte(f_out, 0);
+ }
+ write_byte(f_out, 0);
+ } else {
+ for (i = 0; i < sargc; i++)
+ io_printf(f_out, "%s\n", sargs[i]);
+ write_sbuf(f_out, "\n");
+ }
+
+ if (protect_args)
+ send_protected_args(f_out, sargs);
+
+ if (protocol_version < 23) {
+ if (protocol_version == 22 || !am_sender)
+ io_start_multiplex_in(f_in);
+ }
+
+ free(modname);
+
+ return 0;
+}
+
+static char *finish_pre_exec(pid_t pid, int write_fd, int read_fd, char *request,
+ char **early_argv, char **argv)
+{
+ char buf[BIGPATHBUFLEN], *bp;
+ int j = 0, status = -1, msglen = sizeof buf - 1;
+
+ if (!request)
+ request = "(NONE)";
+
+ write_buf(write_fd, request, strlen(request)+1);
+ if (early_argv) {
+ for ( ; *early_argv; early_argv++)
+ write_buf(write_fd, *early_argv, strlen(*early_argv)+1);
+ j = 1; /* Skip arg0 name in argv. */
+ }
+ for ( ; argv[j]; j++)
+ write_buf(write_fd, argv[j], strlen(argv[j])+1);
+ write_byte(write_fd, 0);
+
+ close(write_fd);
+
+ /* Read the stdout from the pre-xfer exec program. This it is only
+ * displayed to the user if the script also returns an error status. */
+ for (bp = buf; msglen > 0; msglen -= j) {
+ if ((j = read(read_fd, bp, msglen)) <= 0) {
+ if (j == 0)
+ break;
+ if (errno == EINTR)
+ continue;
+ break; /* Just ignore the read error for now... */
+ }
+ bp += j;
+ if (j > 1 && bp[-1] == '\n' && bp[-2] == '\r') {
+ bp--;
+ j--;
+ bp[-1] = '\n';
+ }
+ }
+ *bp = '\0';
+
+ close(read_fd);
+
+ if (wait_process(pid, &status, 0) < 0
+ || !WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ char *e;
+ if (asprintf(&e, "pre-xfer exec returned failure (%d)%s%s%s\n%s",
+ status, status < 0 ? ": " : "",
+ status < 0 ? strerror(errno) : "",
+ *buf ? ":" : "", buf) < 0)
+ return "out_of_memory in finish_pre_exec\n";
+ return e;
+ }
+ return NULL;
+}
+
+#ifdef HAVE_PUTENV
+static int read_arg_from_pipe(int fd, char *buf, int limit)
+{
+ char *bp = buf, *eob = buf + limit - 1;
+
+ while (1) {
+ int got = read(fd, bp, 1);
+ if (got != 1) {
+ if (got < 0 && errno == EINTR)
+ continue;
+ return -1;
+ }
+ if (*bp == '\0')
+ break;
+ if (bp < eob)
+ bp++;
+ }
+ *bp = '\0';
+
+ return bp - buf;
+}
+#endif
+
+static int path_failure(int f_out, const char *dir, BOOL was_chdir)
+{
+ if (was_chdir)
+ rsyserr(FLOG, errno, "chdir %s failed\n", dir);
+ else
+ rprintf(FLOG, "normalize_path(%s) failed\n", dir);
+ io_printf(f_out, "@ERROR: chdir failed\n");
+ return -1;
+}
+
+static int add_a_group(int f_out, const char *gname)
+{
+ gid_t gid;
+ if (!group_to_gid(gname, &gid, True)) {
+ rprintf(FLOG, "Invalid gid %s\n", gname);
+ io_printf(f_out, "@ERROR: invalid gid %s\n", gname);
+ return -1;
+ }
+ if (gid_count == MAX_GID_LIST) {
+ rprintf(FLOG, "Too many groups specified via gid parameter.\n");
+ io_printf(f_out, "@ERROR: too many groups\n");
+ return -1;
+ }
+ gid_list[gid_count++] = gid;
+ return 0;
+}
+
+#ifdef HAVE_GETGROUPLIST
+static int want_all_groups(int f_out, uid_t uid)
+{
+ const char *err;
+ gid_count = MAX_GID_LIST;
+ if ((err = getallgroups(uid, gid_list, &gid_count)) != NULL) {
+ rsyserr(FLOG, errno, "%s", err);
+ io_printf(f_out, "@ERROR: %s\n", err);
+ return -1;
+ }
+ return 0;
+}
+#elif defined HAVE_INITGROUPS
+static struct passwd *want_all_groups(int f_out, uid_t uid)
+{
+ struct passwd *pw;
+ if ((pw = getpwuid(uid)) == NULL) {
+ rsyserr(FLOG, errno, "getpwuid failed");
+ io_printf(f_out, "@ERROR: getpwuid failed\n");
+ return NULL;
+ }
+ /* Start with the default group and initgroups() will add the reset. */
+ gid_count = 1;
+ gid_list[0] = pw->pw_gid;
+ return pw;
+}
+#endif
+
+static void set_env_str(const char *var, const char *str)
+{
+#ifdef HAVE_PUTENV
+ char *mem;
+ if (asprintf(&mem, "%s=%s", var, str) < 0)
+ out_of_memory("set_env_str");
+ putenv(mem);
+#endif
+}
+
+#ifdef HAVE_PUTENV
+static void set_env_num(const char *var, long num)
+{
+ char *mem;
+ if (asprintf(&mem, "%s=%ld", var, num) < 0)
+ out_of_memory("set_env_num");
+ putenv(mem);
+}
+#endif
+
+static int rsync_module(int f_in, int f_out, int i, const char *addr, const char *host)
+{
+ int argc;
+ char **argv, **orig_argv, **orig_early_argv, *module_chdir;
+ char line[BIGPATHBUFLEN];
+#if defined HAVE_INITGROUPS && !defined HAVE_GETGROUPLIST
+ struct passwd *pw = NULL;
+#endif
+ uid_t uid;
+ int set_uid;
+ char *p, *err_msg = NULL;
+ char *name = lp_name(i);
+ int use_chroot = lp_use_chroot(i);
+ int ret, pre_exec_arg_fd = -1, pre_exec_error_fd = -1;
+ int save_munge_symlinks;
+ pid_t pre_exec_pid = 0;
+ char *request = NULL;
+
+ set_env_str("RSYNC_MODULE_NAME", name);
+
+#ifdef ICONV_OPTION
+ iconv_opt = lp_charset(i);
+ if (*iconv_opt)
+ setup_iconv();
+ iconv_opt = NULL;
+#endif
+
+ /* If reverse lookup is disabled globally but enabled for this module,
+ * we need to do it now before the access check. */
+ if (host == undetermined_hostname && lp_reverse_lookup(i))
+ host = client_name(f_in);
+ set_env_str("RSYNC_HOST_NAME", host);
+ set_env_str("RSYNC_HOST_ADDR", addr);
+
+ if (!allow_access(addr, &host, i)) {
+ rprintf(FLOG, "rsync denied on module %s from %s (%s)\n",
+ name, host, addr);
+ if (!lp_list(i))
+ io_printf(f_out, "@ERROR: Unknown module '%s'\n", name);
+ else {
+ io_printf(f_out,
+ "@ERROR: access denied to %s from %s (%s)\n",
+ name, host, addr);
+ }
+ return -1;
+ }
+
+ if (am_daemon && am_server) {
+ rprintf(FLOG, "rsync allowed access on module %s from %s (%s)\n",
+ name, host, addr);
+ }
+
+ if (!claim_connection(lp_lock_file(i), lp_max_connections(i))) {
+ if (errno) {
+ rsyserr(FLOG, errno, "failed to open lock file %s",
+ lp_lock_file(i));
+ io_printf(f_out, "@ERROR: failed to open lock file\n");
+ } else {
+ rprintf(FLOG, "max connections (%d) reached\n",
+ lp_max_connections(i));
+ io_printf(f_out, "@ERROR: max connections (%d) reached -- try again later\n",
+ lp_max_connections(i));
+ }
+ return -1;
+ }
+
+ read_only = lp_read_only(i); /* may also be overridden by auth_server() */
+ auth_user = auth_server(f_in, f_out, i, host, addr, "@RSYNCD: AUTHREQD ");
+
+ if (!auth_user) {
+ io_printf(f_out, "@ERROR: auth failed on module %s\n", name);
+ return -1;
+ }
+ set_env_str("RSYNC_USER_NAME", auth_user);
+
+ module_id = i;
+
+ if (lp_transfer_logging(i) && !logfile_format)
+ logfile_format = lp_log_format(i);
+ if (log_format_has(logfile_format, 'i'))
+ logfile_format_has_i = 1;
+ if (logfile_format_has_i || log_format_has(logfile_format, 'o'))
+ logfile_format_has_o_or_i = 1;
+
+ uid = MY_UID();
+ am_root = (uid == 0);
+
+ p = *lp_uid(i) ? lp_uid(i) : am_root ? NOBODY_USER : NULL;
+ if (p) {
+ if (!user_to_uid(p, &uid, True)) {
+ rprintf(FLOG, "Invalid uid %s\n", p);
+ io_printf(f_out, "@ERROR: invalid uid %s\n", p);
+ return -1;
+ }
+ set_uid = 1;
+ } else
+ set_uid = 0;
+
+ p = *lp_gid(i) ? strtok(lp_gid(i), ", ") : NULL;
+ if (p) {
+ /* The "*" gid must be the first item in the list. */
+ if (strcmp(p, "*") == 0) {
+#ifdef HAVE_GETGROUPLIST
+ if (want_all_groups(f_out, uid) < 0)
+ return -1;
+#elif defined HAVE_INITGROUPS
+ if ((pw = want_all_groups(f_out, uid)) == NULL)
+ return -1;
+#else
+ rprintf(FLOG, "This rsync does not support a gid of \"*\"\n");
+ io_printf(f_out, "@ERROR: invalid gid setting.\n");
+ return -1;
+#endif
+ } else if (add_a_group(f_out, p) < 0)
+ return -1;
+ while ((p = strtok(NULL, ", ")) != NULL) {
+#if defined HAVE_INITGROUPS && !defined HAVE_GETGROUPLIST
+ if (pw) {
+ rprintf(FLOG, "This rsync cannot add groups after \"*\".\n");
+ io_printf(f_out, "@ERROR: invalid gid setting.\n");
+ return -1;
+ }
+#endif
+ if (add_a_group(f_out, p) < 0)
+ return -1;
+ }
+ } else if (am_root) {
+ if (add_a_group(f_out, NOBODY_GROUP) < 0)
+ return -1;
+ }
+
+ module_dir = lp_path(i);
+ if (*module_dir == '\0') {
+ rprintf(FLOG, "No path specified for module %s\n", name);
+ io_printf(f_out, "@ERROR: no path setting.\n");
+ return -1;
+ }
+ if (use_chroot) {
+ if ((p = strstr(module_dir, "/./")) != NULL) {
+ *p = '\0'; /* Temporary... */
+ if (!(module_chdir = normalize_path(module_dir, True, NULL)))
+ return path_failure(f_out, module_dir, False);
+ *p = '/';
+ if (!(p = normalize_path(p + 2, True, &module_dirlen)))
+ return path_failure(f_out, strstr(module_dir, "/./"), False);
+ if (!(full_module_path = normalize_path(module_dir, False, NULL)))
+ full_module_path = module_dir;
+ module_dir = p;
+ } else {
+ if (!(module_chdir = normalize_path(module_dir, False, NULL)))
+ return path_failure(f_out, module_dir, False);
+ full_module_path = module_chdir;
+ module_dir = "/";
+ module_dirlen = 1;
+ }
+ } else {
+ if (!(module_chdir = normalize_path(module_dir, False, &module_dirlen)))
+ return path_failure(f_out, module_dir, False);
+ full_module_path = module_dir = module_chdir;
+ }
+ set_env_str("RSYNC_MODULE_PATH", full_module_path);
+
+ if (module_dirlen == 1) {
+ module_dirlen = 0;
+ set_filter_dir("/", 1);
+ } else
+ set_filter_dir(module_dir, module_dirlen);
+
+ p = lp_filter(i);
+ parse_filter_str(&daemon_filter_list, p, rule_template(FILTRULE_WORD_SPLIT),
+ XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3);
+
+ p = lp_include_from(i);
+ parse_filter_file(&daemon_filter_list, p, rule_template(FILTRULE_INCLUDE),
+ XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3 | XFLG_OLD_PREFIXES | XFLG_FATAL_ERRORS);
+
+ p = lp_include(i);
+ parse_filter_str(&daemon_filter_list, p,
+ rule_template(FILTRULE_INCLUDE | FILTRULE_WORD_SPLIT),
+ XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3 | XFLG_OLD_PREFIXES);
+
+ p = lp_exclude_from(i);
+ parse_filter_file(&daemon_filter_list, p, rule_template(0),
+ XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3 | XFLG_OLD_PREFIXES | XFLG_FATAL_ERRORS);
+
+ p = lp_exclude(i);
+ parse_filter_str(&daemon_filter_list, p, rule_template(FILTRULE_WORD_SPLIT),
+ XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3 | XFLG_OLD_PREFIXES);
+
+ log_init(1);
+
+#ifdef HAVE_PUTENV
+ if (*lp_prexfer_exec(i) || *lp_postxfer_exec(i)) {
+ int status;
+
+ /* For post-xfer exec, fork a new process to run the rsync
+ * daemon while this process waits for the exit status and
+ * runs the indicated command at that point. */
+ if (*lp_postxfer_exec(i)) {
+ pid_t pid = fork();
+ if (pid < 0) {
+ rsyserr(FLOG, errno, "fork failed");
+ io_printf(f_out, "@ERROR: fork failed\n");
+ return -1;
+ }
+ if (pid) {
+ close(f_in);
+ if (f_out != f_in)
+ close(f_out);
+ set_env_num("RSYNC_PID", (long)pid);
+ if (wait_process(pid, &status, 0) < 0)
+ status = -1;
+ set_env_num("RSYNC_RAW_STATUS", status);
+ if (WIFEXITED(status))
+ status = WEXITSTATUS(status);
+ else
+ status = -1;
+ set_env_num("RSYNC_EXIT_STATUS", status);
+ if (system(lp_postxfer_exec(i)) < 0)
+ status = -1;
+ _exit(status);
+ }
+ }
+ /* For pre-xfer exec, fork a child process to run the indicated
+ * command, though it first waits for the parent process to
+ * send us the user's request via a pipe. */
+ if (*lp_prexfer_exec(i)) {
+ int arg_fds[2], error_fds[2];
+ set_env_num("RSYNC_PID", (long)getpid());
+ if (pipe(arg_fds) < 0 || pipe(error_fds) < 0 || (pre_exec_pid = fork()) < 0) {
+ rsyserr(FLOG, errno, "pre-xfer exec preparation failed");
+ io_printf(f_out, "@ERROR: pre-xfer exec preparation failed\n");
+ return -1;
+ }
+ if (pre_exec_pid == 0) {
+ char buf[BIGPATHBUFLEN];
+ int j, len;
+ close(arg_fds[1]);
+ close(error_fds[0]);
+ pre_exec_arg_fd = arg_fds[0];
+ pre_exec_error_fd = error_fds[1];
+ set_blocking(pre_exec_arg_fd);
+ set_blocking(pre_exec_error_fd);
+ len = read_arg_from_pipe(pre_exec_arg_fd, buf, BIGPATHBUFLEN);
+ if (len <= 0)
+ _exit(1);
+ set_env_str("RSYNC_REQUEST", buf);
+ for (j = 0; ; j++) {
+ len = read_arg_from_pipe(pre_exec_arg_fd, buf,
+ BIGPATHBUFLEN);
+ if (len <= 0) {
+ if (!len)
+ break;
+ _exit(1);
+ }
+ if (asprintf(&p, "RSYNC_ARG%d=%s", j, buf) >= 0)
+ putenv(p);
+ }
+ close(pre_exec_arg_fd);
+ close(STDIN_FILENO);
+ dup2(pre_exec_error_fd, STDOUT_FILENO);
+ close(pre_exec_error_fd);
+ status = system(lp_prexfer_exec(i));
+ if (!WIFEXITED(status))
+ _exit(1);
+ _exit(WEXITSTATUS(status));
+ }
+ close(arg_fds[0]);
+ close(error_fds[1]);
+ pre_exec_arg_fd = arg_fds[1];
+ pre_exec_error_fd = error_fds[0];
+ set_blocking(pre_exec_arg_fd);
+ set_blocking(pre_exec_error_fd);
+ }
+ }
+#endif
+
+ if (use_chroot) {
+ /*
+ * XXX: The 'use chroot' flag is a fairly reliable
+ * source of confusion, because it fails under two
+ * important circumstances: running as non-root,
+ * running on Win32 (or possibly others). On the
+ * other hand, if you are running as root, then it
+ * might be better to always use chroot.
+ *
+ * So, perhaps if we can't chroot we should just issue
+ * a warning, unless a "require chroot" flag is set,
+ * in which case we fail.
+ */
+ if (chroot(module_chdir)) {
+ rsyserr(FLOG, errno, "chroot %s failed", module_chdir);
+ io_printf(f_out, "@ERROR: chroot failed\n");
+ return -1;
+ }
+ module_chdir = module_dir;
+ }
+
+ if (!change_dir(module_chdir, CD_NORMAL))
+ return path_failure(f_out, module_chdir, True);
+ if (module_dirlen || !use_chroot)
+ sanitize_paths = 1;
+
+ if ((munge_symlinks = lp_munge_symlinks(i)) < 0)
+ munge_symlinks = !use_chroot || module_dirlen;
+ if (munge_symlinks) {
+ STRUCT_STAT st;
+ char prefix[SYMLINK_PREFIX_LEN]; /* NOT +1 ! */
+ strlcpy(prefix, SYMLINK_PREFIX, sizeof prefix); /* trim the trailing slash */
+ if (do_stat(prefix, &st) == 0 && S_ISDIR(st.st_mode)) {
+ rprintf(FLOG, "Symlink munging is unsafe when a %s directory exists.\n",
+ prefix);
+ io_printf(f_out, "@ERROR: daemon security issue -- contact admin\n", name);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+ }
+
+ if (gid_count) {
+ if (setgid(gid_list[0])) {
+ rsyserr(FLOG, errno, "setgid %ld failed", (long)gid_list[0]);
+ io_printf(f_out, "@ERROR: setgid failed\n");
+ return -1;
+ }
+#ifdef HAVE_SETGROUPS
+ /* Set the group(s) we want to be active. */
+ if (setgroups(gid_count, gid_list)) {
+ rsyserr(FLOG, errno, "setgroups failed");
+ io_printf(f_out, "@ERROR: setgroups failed\n");
+ return -1;
+ }
+#endif
+#if defined HAVE_INITGROUPS && !defined HAVE_GETGROUPLIST
+ /* pw is set if the user wants all the user's groups. */
+ if (pw && initgroups(pw->pw_name, pw->pw_gid) < 0) {
+ rsyserr(FLOG, errno, "initgroups failed");
+ io_printf(f_out, "@ERROR: initgroups failed\n");
+ return -1;
+ }
+#endif
+ }
+
+ if (set_uid) {
+ if (setuid(uid) < 0
+#ifdef HAVE_SETEUID
+ || seteuid(uid) < 0
+#endif
+ ) {
+ rsyserr(FLOG, errno, "setuid %ld failed", (long)uid);
+ io_printf(f_out, "@ERROR: setuid failed\n");
+ return -1;
+ }
+
+ am_root = (MY_UID() == 0);
+ }
+
+ if (lp_temp_dir(i) && *lp_temp_dir(i)) {
+ tmpdir = lp_temp_dir(i);
+ if (strlen(tmpdir) >= MAXPATHLEN - 10) {
+ rprintf(FLOG,
+ "the 'temp dir' value for %s is WAY too long -- ignoring.\n",
+ name);
+ tmpdir = NULL;
+ }
+ }
+
+ io_printf(f_out, "@RSYNCD: OK\n");
+
+ read_args(f_in, name, line, sizeof line, rl_nulls, &argv, &argc, &request);
+ orig_argv = argv;
+
+ save_munge_symlinks = munge_symlinks;
+
+ reset_output_levels(); /* future verbosity is controlled by client options */
+ ret = parse_arguments(&argc, (const char ***) &argv);
+ if (protect_args && ret) {
+ orig_early_argv = orig_argv;
+ protect_args = 2;
+ read_args(f_in, name, line, sizeof line, 1, &argv, &argc, &request);
+ orig_argv = argv;
+ ret = parse_arguments(&argc, (const char ***) &argv);
+ } else
+ orig_early_argv = NULL;
+
+ munge_symlinks = save_munge_symlinks; /* The client mustn't control this. */
+
+ if (pre_exec_pid) {
+ err_msg = finish_pre_exec(pre_exec_pid, pre_exec_arg_fd, pre_exec_error_fd,
+ request, orig_early_argv, orig_argv);
+ }
+
+ if (orig_early_argv)
+ free(orig_early_argv);
+
+ am_server = 1; /* Don't let someone try to be tricky. */
+ quiet = 0;
+ if (lp_ignore_errors(module_id))
+ ignore_errors = 1;
+ if (write_batch < 0)
+ dry_run = 1;
+
+ if (lp_fake_super(i)) {
+ if (preserve_xattrs > 1)
+ preserve_xattrs = 1;
+ am_root = -1;
+ } else if (am_root < 0) /* Treat --fake-super from client as --super. */
+ am_root = 2;
+
+ if (filesfrom_fd == 0)
+ filesfrom_fd = f_in;
+
+ if (request) {
+ if (*auth_user) {
+ rprintf(FLOG, "rsync %s %s from %s@%s (%s)\n",
+ am_sender ? "on" : "to",
+ request, auth_user, host, addr);
+ } else {
+ rprintf(FLOG, "rsync %s %s from %s (%s)\n",
+ am_sender ? "on" : "to",
+ request, host, addr);
+ }
+ free(request);
+ }
+
+#ifndef DEBUG
+ /* don't allow the logs to be flooded too fast */
+ limit_output_verbosity(lp_max_verbosity(i));
+#endif
+
+ if (protocol_version < 23
+ && (protocol_version == 22 || am_sender))
+ io_start_multiplex_out(f_out);
+ else if (!ret || err_msg) {
+ /* We have to get I/O multiplexing started so that we can
+ * get the error back to the client. This means getting
+ * the protocol setup finished first in later versions. */
+ setup_protocol(f_out, f_in);
+ if (!am_sender) {
+ /* Since we failed in our option parsing, we may not
+ * have finished parsing that the client sent us a
+ * --files-from option, so look for it manually.
+ * Without this, the socket would be in the wrong
+ * state for the upcoming error message. */
+ if (!files_from) {
+ int i;
+ for (i = 0; i < argc; i++) {
+ if (strncmp(argv[i], "--files-from", 12) == 0) {
+ files_from = "";
+ break;
+ }
+ }
+ }
+ if (files_from)
+ write_byte(f_out, 0);
+ }
+ io_start_multiplex_out(f_out);
+ }
+
+ if (!ret || err_msg) {
+ if (err_msg) {
+ while ((p = strchr(err_msg, '\n')) != NULL) {
+ int len = p - err_msg + 1;
+ rwrite(FERROR, err_msg, len, 0);
+ err_msg += len;
+ }
+ if (*err_msg)
+ rprintf(FERROR, "%s\n", err_msg);
+ } else
+ option_error();
+ msleep(400);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+
+#ifdef ICONV_OPTION
+ if (!iconv_opt) {
+ if (ic_send != (iconv_t)-1) {
+ iconv_close(ic_send);
+ ic_send = (iconv_t)-1;
+ }
+ if (ic_recv != (iconv_t)-1) {
+ iconv_close(ic_recv);
+ ic_recv = (iconv_t)-1;
+ }
+ }
+#endif
+
+ if (!numeric_ids
+ && (use_chroot ? lp_numeric_ids(i) != False : lp_numeric_ids(i) == True))
+ numeric_ids = -1; /* Set --numeric-ids w/o breaking protocol. */
+
+ if (lp_timeout(i) && (!io_timeout || lp_timeout(i) < io_timeout))
+ set_io_timeout(lp_timeout(i));
+
+ /* If we have some incoming/outgoing chmod changes, append them to
+ * any user-specified changes (making our changes have priority).
+ * We also get a pointer to just our changes so that a receiver
+ * process can use them separately if --perms wasn't specified. */
+ if (am_sender)
+ p = lp_outgoing_chmod(i);
+ else
+ p = lp_incoming_chmod(i);
+ if (*p && !(daemon_chmod_modes = parse_chmod(p, &chmod_modes))) {
+ rprintf(FLOG, "Invalid \"%sing chmod\" directive: %s\n",
+ am_sender ? "outgo" : "incom", p);
+ }
+
+ start_server(f_in, f_out, argc, argv);
+
+ return 0;
+}
+
+/* send a list of available modules to the client. Don't list those
+ with "list = False". */
+static void send_listing(int fd)
+{
+ int n = lp_num_modules();
+ int i;
+
+ for (i = 0; i < n; i++) {
+ if (lp_list(i))
+ io_printf(fd, "%-15s\t%s\n", lp_name(i), lp_comment(i));
+ }
+
+ if (protocol_version >= 25)
+ io_printf(fd,"@RSYNCD: EXIT\n");
+}
+
+static int load_config(int globals_only)
+{
+ if (!config_file) {
+ if (am_server && am_root <= 0)
+ config_file = RSYNCD_USERCONF;
+ else
+ config_file = RSYNCD_SYSCONF;
+ }
+ return lp_load(config_file, globals_only);
+}
+
+/* this is called when a connection is established to a client
+ and we want to start talking. The setup of the system is done from
+ here */
+int start_daemon(int f_in, int f_out)
+{
+ char line[1024];
+ const char *addr, *host;
+ int i;
+
+ io_set_sock_fds(f_in, f_out);
+
+ /* We must load the config file before calling any function that
+ * might cause log-file output to occur. This ensures that the
+ * "log file" param gets honored for the 2 non-forked use-cases
+ * (when rsync is run by init and run by a remote shell). */
+ if (!load_config(0))
+ exit_cleanup(RERR_SYNTAX);
+
+ addr = client_addr(f_in);
+ host = lp_reverse_lookup(-1) ? client_name(f_in) : undetermined_hostname;
+ rprintf(FLOG, "connect from %s (%s)\n", host, addr);
+
+ if (!am_server) {
+ set_socket_options(f_in, "SO_KEEPALIVE");
+ set_nonblocking(f_in);
+ }
+
+ if (exchange_protocols(f_in, f_out, line, sizeof line, 0) < 0)
+ return -1;
+
+ line[0] = 0;
+ if (!read_line_old(f_in, line, sizeof line, 0))
+ return -1;
+
+ if (!*line || strcmp(line, "#list") == 0) {
+ rprintf(FLOG, "module-list request from %s (%s)\n",
+ host, addr);
+ send_listing(f_out);
+ return -1;
+ }
+
+ if (*line == '#') {
+ /* it's some sort of command that I don't understand */
+ io_printf(f_out, "@ERROR: Unknown command '%s'\n", line);
+ return -1;
+ }
+
+ if ((i = lp_number(line)) < 0) {
+ rprintf(FLOG, "unknown module '%s' tried from %s (%s)\n",
+ line, host, addr);
+ io_printf(f_out, "@ERROR: Unknown module '%s'\n", line);
+ return -1;
+ }
+
+#ifdef HAVE_SIGACTION
+ sigact.sa_flags = SA_NOCLDSTOP;
+#endif
+ SIGACTION(SIGCHLD, remember_children);
+
+ return rsync_module(f_in, f_out, i, addr, host);
+}
+
+static void create_pid_file(void)
+{
+ char *pid_file = lp_pid_file();
+ char pidbuf[16];
+ pid_t pid = getpid();
+ int fd, len;
+
+ if (!pid_file || !*pid_file)
+ return;
+
+ cleanup_set_pid(pid);
+ if ((fd = do_open(pid_file, O_WRONLY|O_CREAT|O_EXCL, 0666)) == -1) {
+ failure:
+ cleanup_set_pid(0);
+ fprintf(stderr, "failed to create pid file %s: %s\n", pid_file, strerror(errno));
+ rsyserr(FLOG, errno, "failed to create pid file %s", pid_file);
+ exit_cleanup(RERR_FILEIO);
+ }
+ snprintf(pidbuf, sizeof pidbuf, "%d\n", (int)pid);
+ len = strlen(pidbuf);
+ if (write(fd, pidbuf, len) != len)
+ goto failure;
+ close(fd);
+}
+
+/* Become a daemon, discarding the controlling terminal. */
+static void become_daemon(void)
+{
+ int i;
+ pid_t pid = fork();
+
+ if (pid) {
+ if (pid < 0) {
+ fprintf(stderr, "failed to fork: %s\n", strerror(errno));
+ exit_cleanup(RERR_FILEIO);
+ }
+ _exit(0);
+ }
+
+ create_pid_file();
+
+ /* detach from the terminal */
+#ifdef HAVE_SETSID
+ setsid();
+#elif defined TIOCNOTTY
+ i = open("/dev/tty", O_RDWR);
+ if (i >= 0) {
+ ioctl(i, (int)TIOCNOTTY, (char *)0);
+ close(i);
+ }
+#endif
+ /* make sure that stdin, stdout an stderr don't stuff things
+ * up (library functions, for example) */
+ for (i = 0; i < 3; i++) {
+ close(i);
+ open("/dev/null", O_RDWR);
+ }
+}
+
+int daemon_main(void)
+{
+ if (is_a_socket(STDIN_FILENO)) {
+ int i;
+
+ /* we are running via inetd - close off stdout and
+ * stderr so that library functions (and getopt) don't
+ * try to use them. Redirect them to /dev/null */
+ for (i = 1; i < 3; i++) {
+ close(i);
+ open("/dev/null", O_RDWR);
+ }
+
+ return start_daemon(STDIN_FILENO, STDIN_FILENO);
+ }
+
+ if (!load_config(1)) {
+ fprintf(stderr, "Failed to parse config file: %s\n", config_file);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ set_dparams(0);
+
+ if (no_detach)
+ create_pid_file();
+ else
+ become_daemon();
+
+ if (rsync_port == 0 && (rsync_port = lp_rsync_port()) == 0)
+ rsync_port = RSYNC_PORT;
+ if (bind_address == NULL && *lp_bind_address())
+ bind_address = lp_bind_address();
+
+ log_init(0);
+
+ rprintf(FLOG, "rsyncd version %s starting, listening on port %d\n",
+ RSYNC_VERSION, rsync_port);
+ /* TODO: If listening on a particular address, then show that
+ * address too. In fact, why not just do getnameinfo on the
+ * local address??? */
+
+ start_accept_loop(rsync_port, start_daemon);
+ return -1;
+}
diff --git a/rsync/compat.c b/rsync/compat.c
new file mode 100644
index 0000000..2454937
--- /dev/null
+++ b/rsync/compat.c
@@ -0,0 +1,336 @@
+/*
+ * Compatibility routines for older rsync protocol versions.
+ *
+ * Copyright (C) Andrew Tridgell 1996
+ * Copyright (C) Paul Mackerras 1996
+ * Copyright (C) 2004-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+int remote_protocol = 0;
+int file_extra_cnt = 0; /* count of file-list extras that everyone gets */
+int inc_recurse = 0;
+int compat_flags = 0;
+int use_safe_inc_flist = 0;
+int want_xattr_optim = 0;
+
+extern int am_server;
+extern int am_sender;
+extern int local_server;
+extern int inplace;
+extern int recurse;
+extern int use_qsort;
+extern int allow_inc_recurse;
+extern int preallocate_files;
+extern int append_mode;
+extern int fuzzy_basis;
+extern int read_batch;
+extern int delay_updates;
+extern int checksum_seed;
+extern int basis_dir_cnt;
+extern int prune_empty_dirs;
+extern int protocol_version;
+extern int protect_args;
+extern int preserve_uid;
+extern int preserve_gid;
+extern int preserve_acls;
+extern int preserve_xattrs;
+extern int need_messages_from_generator;
+extern int delete_mode, delete_before, delete_during, delete_after;
+extern char *shell_cmd;
+extern char *partial_dir;
+extern char *dest_option;
+extern char *files_from;
+extern char *filesfrom_host;
+extern filter_rule_list filter_list;
+extern int need_unsorted_flist;
+#ifdef ICONV_OPTION
+extern iconv_t ic_send, ic_recv;
+extern char *iconv_opt;
+#endif
+
+/* These index values are for the file-list's extra-attribute array. */
+int uid_ndx, gid_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
+
+int receiver_symlink_times = 0; /* receiver can set the time on a symlink */
+int sender_symlink_iconv = 0; /* sender should convert symlink content */
+
+#ifdef ICONV_OPTION
+int filesfrom_convert = 0;
+#endif
+
+#define CF_INC_RECURSE (1<<0)
+#define CF_SYMLINK_TIMES (1<<1)
+#define CF_SYMLINK_ICONV (1<<2)
+#define CF_SAFE_FLIST (1<<3)
+#define CF_AVOID_XATTR_OPTIM (1<<4)
+
+static const char *client_info;
+
+/* The server makes sure that if either side only supports a pre-release
+ * version of a protocol, that both sides must speak a compatible version
+ * of that protocol for it to be advertised as available. */
+static void check_sub_protocol(void)
+{
+ char *dot;
+ int their_protocol, their_sub;
+#if SUBPROTOCOL_VERSION != 0
+ int our_sub = protocol_version < PROTOCOL_VERSION ? 0 : SUBPROTOCOL_VERSION;
+#else
+ int our_sub = 0;
+#endif
+
+ /* client_info starts with VER.SUB string if client is a pre-release. */
+ if (!(their_protocol = atoi(client_info))
+ || !(dot = strchr(client_info, '.'))
+ || !(their_sub = atoi(dot+1))) {
+#if SUBPROTOCOL_VERSION != 0
+ if (our_sub)
+ protocol_version--;
+#endif
+ return;
+ }
+
+ if (their_protocol < protocol_version) {
+ if (their_sub)
+ protocol_version = their_protocol - 1;
+ return;
+ }
+
+ if (their_protocol > protocol_version)
+ their_sub = 0; /* 0 == final version of older protocol */
+ if (their_sub != our_sub)
+ protocol_version--;
+}
+
+void set_allow_inc_recurse(void)
+{
+ client_info = shell_cmd ? shell_cmd : "";
+
+ if (!recurse || use_qsort)
+ allow_inc_recurse = 0;
+ else if (!am_sender
+ && (delete_before || delete_after
+ || delay_updates || prune_empty_dirs))
+ allow_inc_recurse = 0;
+ else if (am_server && !local_server
+ && (strchr(client_info, 'i') == NULL))
+ allow_inc_recurse = 0;
+}
+
+void setup_protocol(int f_out,int f_in)
+{
+ if (am_sender)
+ file_extra_cnt += PTR_EXTRA_CNT;
+ else
+ file_extra_cnt++;
+ if (preserve_uid)
+ uid_ndx = ++file_extra_cnt;
+ if (preserve_gid)
+ gid_ndx = ++file_extra_cnt;
+ if (preserve_acls && !am_sender)
+ acls_ndx = ++file_extra_cnt;
+ if (preserve_xattrs)
+ xattrs_ndx = ++file_extra_cnt;
+
+ if (am_server)
+ set_allow_inc_recurse();
+
+ if (remote_protocol == 0) {
+ if (am_server && !local_server)
+ check_sub_protocol();
+ if (!read_batch)
+ write_int(f_out, protocol_version);
+ remote_protocol = read_int(f_in);
+ if (protocol_version > remote_protocol)
+ protocol_version = remote_protocol;
+ }
+ if (read_batch && remote_protocol > protocol_version) {
+ rprintf(FERROR, "The protocol version in the batch file is too new (%d > %d).\n",
+ remote_protocol, protocol_version);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ if (DEBUG_GTE(PROTO, 1)) {
+ rprintf(FINFO, "(%s) Protocol versions: remote=%d, negotiated=%d\n",
+ am_server? "Server" : "Client", remote_protocol, protocol_version);
+ }
+ if (remote_protocol < MIN_PROTOCOL_VERSION
+ || remote_protocol > MAX_PROTOCOL_VERSION) {
+ rprintf(FERROR,"protocol version mismatch -- is your shell clean?\n");
+ rprintf(FERROR,"(see the rsync man page for an explanation)\n");
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ if (remote_protocol < OLD_PROTOCOL_VERSION) {
+ rprintf(FINFO,"%s is very old version of rsync, upgrade recommended.\n",
+ am_server? "Client" : "Server");
+ }
+ if (protocol_version < MIN_PROTOCOL_VERSION) {
+ rprintf(FERROR, "--protocol must be at least %d on the %s.\n",
+ MIN_PROTOCOL_VERSION, am_server? "Server" : "Client");
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ if (protocol_version > PROTOCOL_VERSION) {
+ rprintf(FERROR, "--protocol must be no more than %d on the %s.\n",
+ PROTOCOL_VERSION, am_server? "Server" : "Client");
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ if (read_batch)
+ check_batch_flags();
+
+#ifndef SUPPORT_PREALLOCATION
+ if (preallocate_files && !am_sender) {
+ rprintf(FERROR, "preallocation is not supported on this %s\n",
+ am_server ? "Server" : "Client");
+ exit_cleanup(RERR_SYNTAX);
+ }
+#endif
+
+ if (protocol_version < 30) {
+ if (append_mode == 1)
+ append_mode = 2;
+ if (preserve_acls && !local_server) {
+ rprintf(FERROR,
+ "--acls requires protocol 30 or higher"
+ " (negotiated %d).\n",
+ protocol_version);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ if (preserve_xattrs && !local_server) {
+ rprintf(FERROR,
+ "--xattrs requires protocol 30 or higher"
+ " (negotiated %d).\n",
+ protocol_version);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ }
+
+ if (delete_mode && !(delete_before+delete_during+delete_after)) {
+ if (protocol_version < 30)
+ delete_before = 1;
+ else
+ delete_during = 1;
+ }
+
+ if (protocol_version < 29) {
+ if (fuzzy_basis) {
+ rprintf(FERROR,
+ "--fuzzy requires protocol 29 or higher"
+ " (negotiated %d).\n",
+ protocol_version);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ if (basis_dir_cnt && inplace) {
+ rprintf(FERROR,
+ "%s with --inplace requires protocol 29 or higher"
+ " (negotiated %d).\n",
+ dest_option, protocol_version);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ if (basis_dir_cnt > 1) {
+ rprintf(FERROR,
+ "Using more than one %s option requires protocol"
+ " 29 or higher (negotiated %d).\n",
+ dest_option, protocol_version);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ if (prune_empty_dirs) {
+ rprintf(FERROR,
+ "--prune-empty-dirs requires protocol 29 or higher"
+ " (negotiated %d).\n",
+ protocol_version);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ } else if (protocol_version >= 30) {
+ if (am_server) {
+ compat_flags = allow_inc_recurse ? CF_INC_RECURSE : 0;
+#ifdef CAN_SET_SYMLINK_TIMES
+ compat_flags |= CF_SYMLINK_TIMES;
+#endif
+#ifdef ICONV_OPTION
+ compat_flags |= CF_SYMLINK_ICONV;
+#endif
+ if (local_server || strchr(client_info, 'f') != NULL)
+ compat_flags |= CF_SAFE_FLIST;
+ if (local_server || strchr(client_info, 'x') != NULL)
+ compat_flags |= CF_AVOID_XATTR_OPTIM;
+ write_byte(f_out, compat_flags);
+ } else
+ compat_flags = read_byte(f_in);
+ /* The inc_recurse var MUST be set to 0 or 1. */
+ inc_recurse = compat_flags & CF_INC_RECURSE ? 1 : 0;
+ want_xattr_optim = protocol_version >= 31 && !(compat_flags & CF_AVOID_XATTR_OPTIM);
+ if (am_sender) {
+ receiver_symlink_times = am_server
+ ? strchr(client_info, 'L') != NULL
+ : !!(compat_flags & CF_SYMLINK_TIMES);
+ }
+#ifdef CAN_SET_SYMLINK_TIMES
+ else
+ receiver_symlink_times = 1;
+#endif
+#ifdef ICONV_OPTION
+ sender_symlink_iconv = iconv_opt && (am_server
+ ? local_server || strchr(client_info, 's') != NULL
+ : !!(compat_flags & CF_SYMLINK_ICONV));
+#endif
+ if (inc_recurse && !allow_inc_recurse) {
+ /* This should only be able to happen in a batch. */
+ fprintf(stderr,
+ "Incompatible options specified for inc-recursive %s.\n",
+ read_batch ? "batch file" : "connection");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ use_safe_inc_flist = (compat_flags & CF_SAFE_FLIST) || protocol_version >= 31;
+ need_messages_from_generator = 1;
+#ifdef CAN_SET_SYMLINK_TIMES
+ } else if (!am_sender) {
+ receiver_symlink_times = 1;
+#endif
+ }
+
+ if (need_unsorted_flist && (!am_sender || inc_recurse))
+ unsort_ndx = ++file_extra_cnt;
+
+ if (partial_dir && *partial_dir != '/' && (!am_server || local_server)) {
+ int rflags = FILTRULE_NO_PREFIXES | FILTRULE_DIRECTORY;
+ if (!am_sender || protocol_version >= 30)
+ rflags |= FILTRULE_PERISHABLE;
+ parse_filter_str(&filter_list, partial_dir, rule_template(rflags), 0);
+ }
+
+
+#ifdef ICONV_OPTION
+ if (protect_args && files_from) {
+ if (am_sender)
+ filesfrom_convert = filesfrom_host && ic_send != (iconv_t)-1;
+ else
+ filesfrom_convert = !filesfrom_host && ic_recv != (iconv_t)-1;
+ }
+#endif
+
+ if (am_server) {
+ if (!checksum_seed)
+ checksum_seed = time(NULL);
+ write_int(f_out, checksum_seed);
+ } else {
+ checksum_seed = read_int(f_in);
+ }
+}
diff --git a/rsync/config.h.in b/rsync/config.h.in
new file mode 100644
index 0000000..7626273
--- /dev/null
+++ b/rsync/config.h.in
@@ -0,0 +1,769 @@
+/* config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define if building universal (internal helper macro) */
+#undef AC_APPLE_UNIVERSAL_BUILD
+
+/* Define to 1 if link() can hard-link special files. */
+#undef CAN_HARDLINK_SPECIAL
+
+/* Define to 1 if link() can hard-link symlinks. */
+#undef CAN_HARDLINK_SYMLINK
+
+/* Define to 1 if chown modifies symlinks. */
+#undef CHOWN_MODIFIES_SYMLINK
+
+/* Undefine if you do not want locale features. By default this is defined. */
+#undef CONFIG_LOCALE
+
+/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
+ systems. This function is required for `alloca.c' support on those systems.
+ */
+#undef CRAY_STACKSEG_END
+
+/* Define to 1 if using `alloca.c'. */
+#undef C_ALLOCA
+
+/* Define to 1 if using external zlib */
+#undef EXTERNAL_ZLIB
+
+/* Used to make "checker" understand that FD_ZERO() clears memory. */
+#undef FORCE_FD_ZERO_MEMSET
+
+/* Define to the type of elements in the array set by `getgroups'. Usually
+ this is either `int' or `gid_t'. */
+#undef GETGROUPS_T
+
+/* Define to 1 if the `getpgrp' function requires zero arguments. */
+#undef GETPGRP_VOID
+
+/* Define to 1 if you have the `aclsort' function. */
+#undef HAVE_ACLSORT
+
+/* true if you have acl_get_perm_np */
+#undef HAVE_ACL_GET_PERM_NP
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_ACL_LIBACL_H
+
+/* true if you have AIX ACLs */
+#undef HAVE_AIX_ACLS
+
+/* Define to 1 if you have `alloca', as a function or macro. */
+#undef HAVE_ALLOCA
+
+/* Define to 1 if you have and it should be used (not on Ultrix).
+ */
+#undef HAVE_ALLOCA_H
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_ARPA_INET_H
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_ARPA_NAMESER_H
+
+/* Define to 1 if you have the `asprintf' function. */
+#undef HAVE_ASPRINTF
+
+/* Define to 1 if you have the `attropen' function. */
+#undef HAVE_ATTROPEN
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_ATTR_XATTR_H
+
+/* Define to 1 if readdir() is broken */
+#undef HAVE_BROKEN_READDIR
+
+/* Define to 1 if vsprintf has a C99-compatible return value */
+#undef HAVE_C99_VSNPRINTF
+
+/* Define to 1 if you have the `chmod' function. */
+#undef HAVE_CHMOD
+
+/* Define to 1 if you have the `chown' function. */
+#undef HAVE_CHOWN
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_COMPAT_H
+
+/* Define to 1 if you have the "connect" function */
+#undef HAVE_CONNECT
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_CTYPE_H
+
+/* Define to 1 if you have the header file, and it defines `DIR'.
+ */
+#undef HAVE_DIRENT_H
+
+/* Define if posix_fallocate is efficient (Cygwin) */
+#undef HAVE_EFFICIENT_POSIX_FALLOCATE
+
+/* Define to 1 if errno is declared in errno.h */
+#undef HAVE_ERRNO_DECL
+
+/* Define to 1 if you have the `extattr_get_link' function. */
+#undef HAVE_EXTATTR_GET_LINK
+
+/* Define to 1 if you have the fallocate function and it compiles and links
+ without error */
+#undef HAVE_FALLOCATE
+
+/* Define to 1 if you have the `fchmod' function. */
+#undef HAVE_FCHMOD
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_FCNTL_H
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_FLOAT_H
+
+/* True if you have FreeBSD xattrs */
+#undef HAVE_FREEBSD_XATTRS
+
+/* Define to 1 if you have the `fstat' function. */
+#undef HAVE_FSTAT
+
+/* Define to 1 if you have the `ftruncate' function. */
+#undef HAVE_FTRUNCATE
+
+/* Define to 1 if you have the "getaddrinfo" function and required types. */
+#undef HAVE_GETADDRINFO
+
+/* Define to 1 if you have the `getcwd' function. */
+#undef HAVE_GETCWD
+
+/* Define to 1 if you have the `getegid' function. */
+#undef HAVE_GETEGID
+
+/* Define to 1 if you have the `geteuid' function. */
+#undef HAVE_GETEUID
+
+/* Define to 1 if you have the `getgrouplist' function. */
+#undef HAVE_GETGROUPLIST
+
+/* Define to 1 if you have the `getgroups' function. */
+#undef HAVE_GETGROUPS
+
+/* Define to 1 if you have the `getpass' function. */
+#undef HAVE_GETPASS
+
+/* Define to 1 if you have the `getpgrp' function. */
+#undef HAVE_GETPGRP
+
+/* Define to 1 if gettimeofday() takes a time-zone arg */
+#undef HAVE_GETTIMEOFDAY_TZ
+
+/* Define to 1 if you have the `getxattr' function. */
+#undef HAVE_GETXATTR
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_GRP_H
+
+/* true if you have HPUX ACLs */
+#undef HAVE_HPUX_ACLS
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_ICONV_H
+
+/* Define to 1 if you have the `iconv_open' function. */
+#undef HAVE_ICONV_OPEN
+
+/* Define to 1 if the system has the type `id_t'. */
+#undef HAVE_ID_T
+
+/* Define to 1 if you have the `inet_ntop' function. */
+#undef HAVE_INET_NTOP
+
+/* Define to 1 if you have the `inet_pton' function. */
+#undef HAVE_INET_PTON
+
+/* Define to 1 if you have the `initgroups' function. */
+#undef HAVE_INITGROUPS
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_INTTYPES_H
+
+/* true if you have IRIX ACLs */
+#undef HAVE_IRIX_ACLS
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_LANGINFO_H
+
+/* Define to 1 if you have the `lchmod' function. */
+#undef HAVE_LCHMOD
+
+/* Define to 1 if you have the `lchown' function. */
+#undef HAVE_LCHOWN
+
+/* Define to 1 if you have the `acl' library (-lacl). */
+#undef HAVE_LIBACL
+
+/* Define to 1 if you have the `attr' library (-lattr). */
+#undef HAVE_LIBATTR
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_LIBCHARSET_H
+
+/* Define to 1 if you have the `inet' library (-linet). */
+#undef HAVE_LIBINET
+
+/* Define to 1 if you have the `nsl' library (-lnsl). */
+#undef HAVE_LIBNSL
+
+/* Define to 1 if you have the `nsl_s' library (-lnsl_s). */
+#undef HAVE_LIBNSL_S
+
+/* Define to 1 if you have the `popt' library (-lpopt). */
+#undef HAVE_LIBPOPT
+
+/* Define to 1 if you have the `resolv' library (-lresolv). */
+#undef HAVE_LIBRESOLV
+
+/* Define to 1 if you have the `sec' library (-lsec). */
+#undef HAVE_LIBSEC
+
+/* Define to 1 if you have the `socket' library (-lsocket). */
+#undef HAVE_LIBSOCKET
+
+/* Define to 1 if you have the `z' library (-lz). */
+#undef HAVE_LIBZ
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_LIMITS_H
+
+/* Define to 1 if you have the `link' function. */
+#undef HAVE_LINK
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_LINUX_FALLOC_H
+
+/* True if you have Linux xattrs */
+#undef HAVE_LINUX_XATTRS
+
+/* Define to 1 if you have the `locale_charset' function. */
+#undef HAVE_LOCALE_CHARSET
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_LOCALE_H
+
+/* Define to 1 if the type `long double' works and has more range or precision
+ than `double'. */
+#undef HAVE_LONG_DOUBLE
+
+/* Define to 1 if the type `long double' works and has more range or precision
+ than `double'. */
+#undef HAVE_LONG_DOUBLE_WIDER
+
+/* Define to 1 if you have the `lseek64' function. */
+#undef HAVE_LSEEK64
+
+/* Define to 1 if you have the `lutimes' function. */
+#undef HAVE_LUTIMES
+
+/* Define to 1 if you have the `mallinfo' function. */
+#undef HAVE_MALLINFO
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_MALLOC_H
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_MCHECK_H
+
+/* Define to 1 if you have the `memmove' function. */
+#undef HAVE_MEMMOVE
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the `mkfifo' function. */
+#undef HAVE_MKFIFO
+
+/* Define to 1 if you have the `mknod' function. */
+#undef HAVE_MKNOD
+
+/* Define to 1 if you have the `mkstemp64' function. */
+#undef HAVE_MKSTEMP64
+
+/* Define to 1 if the system has the type `mode_t'. */
+#undef HAVE_MODE_T
+
+/* Define to 1 if you have the `mtrace' function. */
+#undef HAVE_MTRACE
+
+/* Define to 1 if you have the header file, and it defines `DIR'. */
+#undef HAVE_NDIR_H
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_NETDB_H
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_NETINET_IN_SYSTM_H
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_NETINET_IP_H
+
+/* Define to 1 if you have the `nl_langinfo' function. */
+#undef HAVE_NL_LANGINFO
+
+/* Define to 1 if the system has the type `off_t'. */
+#undef HAVE_OFF_T
+
+/* Define to 1 if you have the `open64' function. */
+#undef HAVE_OPEN64
+
+/* true if you have Mac OS X ACLs */
+#undef HAVE_OSX_ACLS
+
+/* True if you have Mac OS X xattrs */
+#undef HAVE_OSX_XATTRS
+
+/* Define to 1 if the system has the type `pid_t'. */
+#undef HAVE_PID_T
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_POPT_H
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_POPT_POPT_H
+
+/* true if you have posix ACLs */
+#undef HAVE_POSIX_ACLS
+
+/* Define to 1 if you have the `posix_fallocate' function. */
+#undef HAVE_POSIX_FALLOCATE
+
+/* Define to 1 if you have the `putenv' function. */
+#undef HAVE_PUTENV
+
+/* Define to 1 if you have the `readlink' function. */
+#undef HAVE_READLINK
+
+/* Define to 1 if remote shell is remsh, not rsh */
+#undef HAVE_REMSH
+
+/* Define to 1 if mkstemp() is available and works right */
+#undef HAVE_SECURE_MKSTEMP
+
+/* Define to 1 if you have the `setattrlist' function. */
+#undef HAVE_SETATTRLIST
+
+/* Define to 1 if you have the `seteuid' function. */
+#undef HAVE_SETEUID
+
+/* Define to 1 if you have the `setgroups' function. */
+#undef HAVE_SETGROUPS
+
+/* Define to 1 if you have the `setlocale' function. */
+#undef HAVE_SETLOCALE
+
+/* Define to 1 if you have the `setmode' function. */
+#undef HAVE_SETMODE
+
+/* Define to 1 if you have the `setsid' function. */
+#undef HAVE_SETSID
+
+/* Define to 1 if you have the `setvbuf' function. */
+#undef HAVE_SETVBUF
+
+/* Define to 1 if you have the `sigaction' function. */
+#undef HAVE_SIGACTION
+
+/* Define to 1 if you have the `sigprocmask' function. */
+#undef HAVE_SIGPROCMASK
+
+/* Define to 1 if the system has the type `size_t'. */
+#undef HAVE_SIZE_T
+
+/* Define to 1 if you have the `snprintf' function. */
+#undef HAVE_SNPRINTF
+
+/* Do we have sockaddr_in6.sin6_scope_id? */
+#undef HAVE_SOCKADDR_IN6_SCOPE_ID
+
+/* Do we have sockaddr_in.sin_len? */
+#undef HAVE_SOCKADDR_IN_LEN
+
+/* Do we have sockaddr.sa_len? */
+#undef HAVE_SOCKADDR_LEN
+
+/* Do we have sockaddr_un.sun_len? */
+#undef HAVE_SOCKADDR_UN_LEN
+
+/* Define to 1 if you have the "socketpair" function */
+#undef HAVE_SOCKETPAIR
+
+/* true if you have solaris ACLs */
+#undef HAVE_SOLARIS_ACLS
+
+/* True if you have Solaris xattrs */
+#undef HAVE_SOLARIS_XATTRS
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the `strcasecmp' function. */
+#undef HAVE_STRCASECMP
+
+/* Define to 1 if you have the `strchr' function. */
+#undef HAVE_STRCHR
+
+/* Define to 1 if you have the `strdup' function. */
+#undef HAVE_STRDUP
+
+/* Define to 1 if you have the `strerror' function. */
+#undef HAVE_STRERROR
+
+/* Define to 1 if you have the `strftime' function. */
+#undef HAVE_STRFTIME
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the `strlcat' function. */
+#undef HAVE_STRLCAT
+
+/* Define to 1 if you have the `strlcpy' function. */
+#undef HAVE_STRLCPY
+
+/* Define to 1 if you have the `strpbrk' function. */
+#undef HAVE_STRPBRK
+
+/* Define to 1 if you have the `strtol' function. */
+#undef HAVE_STRTOL
+
+/* Define to 1 if the system has the type `struct addrinfo'. */
+#undef HAVE_STRUCT_ADDRINFO
+
+/* Define to 1 if the system has the type `struct sockaddr_storage'. */
+#undef HAVE_STRUCT_SOCKADDR_STORAGE
+
+/* Define to 1 if the system has the type `struct stat64'. */
+#undef HAVE_STRUCT_STAT64
+
+/* Define to 1 if `st_mtimensec' is a member of `struct stat'. */
+#undef HAVE_STRUCT_STAT_ST_MTIMENSEC
+
+/* Define to 1 if `st_mtim.tv_nsec' is a member of `struct stat'. */
+#undef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+
+/* Define to 1 if `st_rdev' is a member of `struct stat'. */
+#undef HAVE_STRUCT_STAT_ST_RDEV
+
+/* Define to 1 if you have the "struct utimbuf" type */
+#undef HAVE_STRUCT_UTIMBUF
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_SYS_ACL_H
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_SYS_ATTR_H
+
+/* Define to 1 if you have the header file, and it defines `DIR'.
+ */
+#undef HAVE_SYS_DIR_H
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_SYS_EXTATTR_H
+
+/* Define to 1 if you have the SYS_fallocate syscall number */
+#undef HAVE_SYS_FALLOCATE
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_SYS_FCNTL_H
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_SYS_FILIO_H
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_SYS_IOCTL_H
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_SYS_MODE_H
+
+/* Define to 1 if you have the header file, and it defines `DIR'.
+ */
+#undef HAVE_SYS_NDIR_H
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_SYS_PARAM_H
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_SYS_SELECT_H
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_SYS_SOCKET_H
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_SYS_TIME_H
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_SYS_UNISTD_H
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_SYS_UN_H
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_SYS_WAIT_H
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_SYS_XATTR_H
+
+/* Define to 1 if you have the `tcgetpgrp' function. */
+#undef HAVE_TCGETPGRP
+
+/* true if you have Tru64 ACLs */
+#undef HAVE_TRU64_ACLS
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_UNISTD_H
+
+/* true if you have UnixWare ACLs */
+#undef HAVE_UNIXWARE_ACLS
+
+/* Define to 1 if you have the `utime' function. */
+#undef HAVE_UTIME
+
+/* Define to 1 if you have the `utimensat' function. */
+#undef HAVE_UTIMENSAT
+
+/* Define to 1 if you have the `utimes' function. */
+#undef HAVE_UTIMES
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_UTIME_H
+
+/* Define to 1 if `utime(file, NULL)' sets file's timestamp to the present. */
+#undef HAVE_UTIME_NULL
+
+/* Define to 1 if you have the `vasprintf' function. */
+#undef HAVE_VASPRINTF
+
+/* Define to 1 if you have the `va_copy' function. */
+#undef HAVE_VA_COPY
+
+/* Define to 1 if you have the `vsnprintf' function. */
+#undef HAVE_VSNPRINTF
+
+/* Define to 1 if you have the `wait4' function. */
+#undef HAVE_WAIT4
+
+/* Define to 1 if you have the `waitpid' function. */
+#undef HAVE_WAITPID
+
+/* Define to 1 if you have the header file. */
+#undef HAVE_ZLIB_H
+
+/* Define to 1 if you have the `_acl' function. */
+#undef HAVE__ACL
+
+/* Define to 1 if you have the `_facl' function. */
+#undef HAVE__FACL
+
+/* Define to 1 if you have the `__acl' function. */
+#undef HAVE___ACL
+
+/* Define to 1 if you have the `__facl' function. */
+#undef HAVE___FACL
+
+/* Define to 1 if you have the `__va_copy' function. */
+#undef HAVE___VA_COPY
+
+/* Define as const if the declaration of iconv() needs const. */
+#undef ICONV_CONST
+
+/* Define if you want the --iconv option. Specifing a value will set the
+ default iconv setting (a NULL means no --iconv processing by default). */
+#undef ICONV_OPTION
+
+/* true if you have IPv6 */
+#undef INET6
+
+/* Define to 1 if `major', `minor', and `makedev' are declared in .
+ */
+#undef MAJOR_IN_MKDEV
+
+/* Define to 1 if `major', `minor', and `makedev' are declared in
+ . */
+#undef MAJOR_IN_SYSMACROS
+
+/* Define to 1 if makedev() takes 3 args */
+#undef MAKEDEV_TAKES_3_ARGS
+
+/* Define to 1 if mknod() can create FIFOs. */
+#undef MKNOD_CREATES_FIFOS
+
+/* Define to 1 if mknod() can create sockets. */
+#undef MKNOD_CREATES_SOCKETS
+
+/* unprivileged group for unprivileged user */
+#undef NOBODY_GROUP
+
+/* unprivileged user--e.g. nobody */
+#undef NOBODY_USER
+
+/* True if device files do not support xattrs */
+#undef NO_DEVICE_XATTRS
+
+/* True if special files do not support xattrs */
+#undef NO_SPECIAL_XATTRS
+
+/* True if symlinks do not support user xattrs */
+#undef NO_SYMLINK_USER_XATTRS
+
+/* True if symlinks do not support xattrs */
+#undef NO_SYMLINK_XATTRS
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define as the return type of signal handlers (`int' or `void'). */
+#undef RETSIGTYPE
+
+/* location of configuration file for rsync server */
+#undef RSYNCD_SYSCONF
+
+/* location of rsync on remote machine */
+#undef RSYNC_PATH
+
+/* default -e command */
+#undef RSYNC_RSH
+
+/* Define to 1 if --protected-args should be the default */
+#undef RSYNC_USE_PROTECTED_ARGS
+
+/* rsync release version */
+#undef RSYNC_VERSION
+
+/* Define to 1 if sockets need to be shutdown */
+#undef SHUTDOWN_ALL_SOCKETS
+
+/* Define to 1 if "signed char" is a valid type */
+#undef SIGNED_CHAR_OK
+
+/* The size of `int', as computed by sizeof. */
+#undef SIZEOF_INT
+
+/* The size of `int16_t', as computed by sizeof. */
+#undef SIZEOF_INT16_T
+
+/* The size of `int32_t', as computed by sizeof. */
+#undef SIZEOF_INT32_T
+
+/* The size of `int64_t', as computed by sizeof. */
+#undef SIZEOF_INT64_T
+
+/* The size of `long', as computed by sizeof. */
+#undef SIZEOF_LONG
+
+/* The size of `long long', as computed by sizeof. */
+#undef SIZEOF_LONG_LONG
+
+/* The size of `off64_t', as computed by sizeof. */
+#undef SIZEOF_OFF64_T
+
+/* The size of `off_t', as computed by sizeof. */
+#undef SIZEOF_OFF_T
+
+/* The size of `short', as computed by sizeof. */
+#undef SIZEOF_SHORT
+
+/* The size of `time_t', as computed by sizeof. */
+#undef SIZEOF_TIME_T
+
+/* The size of `uint16_t', as computed by sizeof. */
+#undef SIZEOF_UINT16_T
+
+/* The size of `uint32_t', as computed by sizeof. */
+#undef SIZEOF_UINT32_T
+
+/* If using the C implementation of alloca, define if you know the
+ direction of stack growth for your system; otherwise it will be
+ automatically deduced at runtime.
+ STACK_DIRECTION > 0 => grows toward higher addresses
+ STACK_DIRECTION < 0 => grows toward lower addresses
+ STACK_DIRECTION = 0 => direction of growth unknown */
+#undef STACK_DIRECTION
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Define to 1 to add support for ACLs */
+#undef SUPPORT_ACLS
+
+/* Define to 1 to add support for extended attributes */
+#undef SUPPORT_XATTRS
+
+/* Define to 1 if you can safely include both and . */
+#undef TIME_WITH_SYS_TIME
+
+/* Define to 1 if you want rsync to make use of iconv_open() */
+#undef USE_ICONV_OPEN
+
+/* String to pass to iconv() for the UTF-8 charset. */
+#undef UTF8_CHARSET
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+ significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+# define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+# undef WORDS_BIGENDIAN
+# endif
+#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+#undef _FILE_OFFSET_BITS
+
+/* Define _GNU_SOURCE so that we get all necessary prototypes */
+#undef _GNU_SOURCE
+
+/* Define for large files, on AIX-style hosts. */
+#undef _LARGE_FILES
+
+/* Define to `int' if doesn't define. */
+#undef gid_t
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+ calls it, or to nothing if 'inline' is not supported under any name. */
+#ifndef __cplusplus
+#undef inline
+#endif
+
+/* Define to `unsigned int' if does not define. */
+#undef size_t
+
+/* type to use in place of socklen_t if not defined */
+#undef socklen_t
+
+/* Define to `int' if doesn't define. */
+#undef uid_t
diff --git a/rsync/connection.c b/rsync/connection.c
new file mode 100644
index 0000000..6281861
--- /dev/null
+++ b/rsync/connection.c
@@ -0,0 +1,46 @@
+/*
+ * Support the max connections option.
+ *
+ * Copyright (C) 1998 Andrew Tridgell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+/* A simple routine to do connection counting. This returns 1 on success
+ * and 0 on failure, with errno also being set if the open() failed (errno
+ * will be 0 if the lock request failed). */
+int claim_connection(char *fname, int max_connections)
+{
+ int fd, i;
+
+ if (max_connections == 0)
+ return 1;
+
+ if ((fd = open(fname, O_RDWR|O_CREAT, 0600)) < 0)
+ return 0;
+
+ /* Find a free spot. */
+ for (i = 0; i < max_connections; i++) {
+ if (lock_range(fd, i*4, 4))
+ return 1;
+ }
+
+ close(fd);
+
+ /* A lock failure needs to return an errno of 0. */
+ errno = 0;
+ return 0;
+}
diff --git a/rsync/csprotocol.txt b/rsync/csprotocol.txt
new file mode 100644
index 0000000..c8dadd4
--- /dev/null
+++ b/rsync/csprotocol.txt
@@ -0,0 +1,88 @@
+This is kind of informal and may be wrong, but it helped me. It's
+basically a summary of clientserver.c and authenticate.c.
+
+ -- Martin Pool
+
+
+This is the protocol used for rsync --daemon; i.e. connections to port
+873 rather than invocations over a remote shell.
+
+When the server accepts a connection, it prints a greeting
+
+ @RSYNCD: .
+
+where is the numeric version (see PROTOCOL_VERSION in rsync.h)
+'.' is a literal period, and is the numeric subprotocol
+version (see SUBPROTOCOL_VERSION -- it will be 0 for final releases).
+Protocols prior to 30 only output alone. The daemon expects
+to see a similar greeting back from the client. For protocols prior to
+30, an absent "." value is assumed to be 0. For protocol
+30, an absent value is a fatal error. The daemon then follows this line
+with a free-format text message-of-the-day (if any is defined).
+
+The server is now in the connected state. The client can either send
+the command
+
+ #list
+
+to get a listing of modules, or the name of a module. After this, the
+connection is now bound to a particular module. Access per host for
+this module is now checked, as is per-module connection limits.
+
+If authentication is required to use this module, the server will say
+
+ @RSYNCD: AUTHREQD
+
+where is a random string of base64 characters. The client
+must respond with
+
+
+
+where is the username they claim to be, and is the
+base64 form of the MD4 hash of challenge+password.
+
+At this point the server applies all remaining constraints before
+handing control to the client, including switching uid/gid, setting up
+include and exclude lists, moving to the root of the module, and doing
+chroot.
+
+If the login is acceptable, then the server will respond with
+
+ @RSYNCD: OK
+
+The client now writes some rsync options, as if it were remotely
+executing the command. The server parses these arguments as if it had
+just been invoked with them, but they're added to the existing state.
+So if the client specifies a list of files to be included or excluded,
+they'll defer to existing limits specified in the server
+configuration.
+
+At this point the client and server both switch to using a
+multiplexing layer across the socket. The main point of this is to
+allow the server to asynchronously pass errors back, while still
+allowing streamed and pipelined data.
+
+Unfortunately, the multiplex protocol is not used at every stage. We
+start up in plain socket mode and then change over by calling
+io_start_buffering. Of course both the client and the server have to
+do this at the same point.
+
+The server then talks to the client as normal across the socket,
+passing checksums, file lists and so on. For documentation of that,
+stay tuned (or write it yourself!).
+
+
+
+------------
+Protocol version changes
+
+30 (2007-10-04, 3.0.0pre1)
+
+ The use of a "." number was added to
+ @RSYNCD: .
+
+25 (2001-08-20, 2.4.7pre2)
+
+ Send an explicit "@RSYNC EXIT" command at the end of the
+ module listing. We never intentionally end the transmission
+ by just closing the socket anymore.
diff --git a/rsync/delete.c b/rsync/delete.c
new file mode 100644
index 0000000..2927a93
--- /dev/null
+++ b/rsync/delete.c
@@ -0,0 +1,240 @@
+/*
+ * Deletion routines used in rsync.
+ *
+ * Copyright (C) 1996-2000 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2002 Martin Pool
+ * Copyright (C) 2003-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+extern int am_root;
+extern int make_backups;
+extern int max_delete;
+extern char *backup_dir;
+extern char *backup_suffix;
+extern int backup_suffix_len;
+extern struct stats stats;
+
+int ignore_perishable = 0;
+int non_perishable_cnt = 0;
+int skipped_deletes = 0;
+
+static inline int is_backup_file(char *fn)
+{
+ int k = strlen(fn) - backup_suffix_len;
+ return k > 0 && strcmp(fn+k, backup_suffix) == 0;
+}
+
+/* The directory is about to be deleted: if DEL_RECURSE is given, delete all
+ * its contents, otherwise just checks for content. Returns DR_SUCCESS or
+ * DR_NOT_EMPTY. Note that fname must point to a MAXPATHLEN buffer! (The
+ * buffer is used for recursion, but returned unchanged.)
+ */
+static enum delret delete_dir_contents(char *fname, uint16 flags)
+{
+ struct file_list *dirlist;
+ enum delret ret;
+ unsigned remainder;
+ void *save_filters;
+ int j, dlen;
+ char *p;
+
+ if (DEBUG_GTE(DEL, 3)) {
+ rprintf(FINFO, "delete_dir_contents(%s) flags=%d\n",
+ fname, flags);
+ }
+
+ dlen = strlen(fname);
+ save_filters = push_local_filters(fname, dlen);
+
+ non_perishable_cnt = 0;
+ dirlist = get_dirlist(fname, dlen, 0);
+ ret = non_perishable_cnt ? DR_NOT_EMPTY : DR_SUCCESS;
+
+ if (!dirlist->used)
+ goto done;
+
+ if (!(flags & DEL_RECURSE)) {
+ ret = DR_NOT_EMPTY;
+ goto done;
+ }
+
+ p = fname + dlen;
+ if (dlen != 1 || *fname != '/')
+ *p++ = '/';
+ remainder = MAXPATHLEN - (p - fname);
+
+ /* We do our own recursion, so make delete_item() non-recursive. */
+ flags = (flags & ~(DEL_RECURSE|DEL_MAKE_ROOM|DEL_NO_UID_WRITE))
+ | DEL_DIR_IS_EMPTY;
+
+ for (j = dirlist->used; j--; ) {
+ struct file_struct *fp = dirlist->files[j];
+
+ if (fp->flags & FLAG_MOUNT_DIR && S_ISDIR(fp->mode)) {
+ if (DEBUG_GTE(DEL, 1)) {
+ rprintf(FINFO,
+ "mount point, %s, pins parent directory\n",
+ f_name(fp, NULL));
+ }
+ ret = DR_NOT_EMPTY;
+ continue;
+ }
+
+ strlcpy(p, fp->basename, remainder);
+ if (!(fp->mode & S_IWUSR) && !am_root && fp->flags & FLAG_OWNED_BY_US)
+ do_chmod(fname, fp->mode | S_IWUSR);
+ /* Save stack by recursing to ourself directly. */
+ if (S_ISDIR(fp->mode)) {
+ if (delete_dir_contents(fname, flags | DEL_RECURSE) != DR_SUCCESS)
+ ret = DR_NOT_EMPTY;
+ }
+ if (delete_item(fname, fp->mode, flags) != DR_SUCCESS)
+ ret = DR_NOT_EMPTY;
+ }
+
+ fname[dlen] = '\0';
+
+ done:
+ flist_free(dirlist);
+ pop_local_filters(save_filters);
+
+ if (ret == DR_NOT_EMPTY) {
+ rprintf(FINFO, "cannot delete non-empty directory: %s\n",
+ fname);
+ }
+ return ret;
+}
+
+/* Delete a file or directory. If DEL_RECURSE is set in the flags, this will
+ * delete recursively.
+ *
+ * Note that fbuf must point to a MAXPATHLEN buffer if the mode indicates it's
+ * a directory! (The buffer is used for recursion, but returned unchanged.)
+ */
+enum delret delete_item(char *fbuf, uint16 mode, uint16 flags)
+{
+ enum delret ret;
+ char *what;
+ int ok;
+
+ if (DEBUG_GTE(DEL, 2)) {
+ rprintf(FINFO, "delete_item(%s) mode=%o flags=%d\n",
+ fbuf, (int)mode, (int)flags);
+ }
+
+ if (flags & DEL_NO_UID_WRITE)
+ do_chmod(fbuf, mode | S_IWUSR);
+
+ if (S_ISDIR(mode) && !(flags & DEL_DIR_IS_EMPTY)) {
+ /* This only happens on the first call to delete_item() since
+ * delete_dir_contents() always calls us w/DEL_DIR_IS_EMPTY. */
+ ignore_perishable = 1;
+ /* If DEL_RECURSE is not set, this just reports emptiness. */
+ ret = delete_dir_contents(fbuf, flags);
+ ignore_perishable = 0;
+ if (ret == DR_NOT_EMPTY || ret == DR_AT_LIMIT)
+ goto check_ret;
+ /* OK: try to delete the directory. */
+ }
+
+ if (!(flags & DEL_MAKE_ROOM) && max_delete >= 0 && stats.deleted_files >= max_delete) {
+ skipped_deletes++;
+ return DR_AT_LIMIT;
+ }
+
+ if (S_ISDIR(mode)) {
+ what = "rmdir";
+ ok = do_rmdir(fbuf) == 0;
+ } else {
+ if (make_backups > 0 && !(flags & DEL_FOR_BACKUP) && (backup_dir || !is_backup_file(fbuf))) {
+ what = "make_backup";
+ ok = make_backup(fbuf, True);
+ if (ok == 2) {
+ what = "unlink";
+ ok = robust_unlink(fbuf) == 0;
+ }
+ } else {
+ what = "unlink";
+ ok = robust_unlink(fbuf) == 0;
+ }
+ }
+
+ if (ok) {
+ if (!(flags & DEL_MAKE_ROOM)) {
+ log_delete(fbuf, mode);
+ stats.deleted_files++;
+ if (S_ISREG(mode)) {
+ /* Nothing more to count */
+ } else if (S_ISDIR(mode))
+ stats.deleted_dirs++;
+#ifdef SUPPORT_LINKS
+ else if (S_ISLNK(mode))
+ stats.deleted_symlinks++;
+#endif
+ else if (IS_DEVICE(mode))
+ stats.deleted_symlinks++;
+ else
+ stats.deleted_specials++;
+ }
+ ret = DR_SUCCESS;
+ } else {
+ if (S_ISDIR(mode) && errno == ENOTEMPTY) {
+ rprintf(FINFO, "cannot delete non-empty directory: %s\n",
+ fbuf);
+ ret = DR_NOT_EMPTY;
+ } else if (errno != ENOENT) {
+ rsyserr(FERROR_XFER, errno, "delete_file: %s(%s) failed",
+ what, fbuf);
+ ret = DR_FAILURE;
+ } else
+ ret = DR_SUCCESS;
+ }
+
+ check_ret:
+ if (ret != DR_SUCCESS && flags & DEL_MAKE_ROOM) {
+ const char *desc;
+ switch (flags & DEL_MAKE_ROOM) {
+ case DEL_FOR_FILE: desc = "regular file"; break;
+ case DEL_FOR_DIR: desc = "directory"; break;
+ case DEL_FOR_SYMLINK: desc = "symlink"; break;
+ case DEL_FOR_DEVICE: desc = "device file"; break;
+ case DEL_FOR_SPECIAL: desc = "special file"; break;
+ default: exit_cleanup(RERR_UNSUPPORTED); /* IMPOSSIBLE */
+ }
+ rprintf(FERROR_XFER, "could not make way for %s %s: %s\n",
+ flags & DEL_FOR_BACKUP ? "backup" : "new",
+ desc, fbuf);
+ }
+ return ret;
+}
+
+uint16 get_del_for_flag(uint16 mode)
+{
+ if (S_ISREG(mode))
+ return DEL_FOR_FILE;
+ if (S_ISDIR(mode))
+ return DEL_FOR_DIR;
+ if (S_ISLNK(mode))
+ return DEL_FOR_SYMLINK;
+ if (IS_DEVICE(mode))
+ return DEL_FOR_DEVICE;
+ if (IS_SPECIAL(mode))
+ return DEL_FOR_SPECIAL;
+ exit_cleanup(RERR_UNSUPPORTED); /* IMPOSSIBLE */
+}
diff --git a/rsync/doc/README-SGML b/rsync/doc/README-SGML
new file mode 100644
index 0000000..ce5a8e1
--- /dev/null
+++ b/rsync/doc/README-SGML
@@ -0,0 +1,20 @@
+Handling the rsync SGML documentation
+
+rsync documentation is now primarily in Docbook format. Docbook is an
+SGML/XML documentation format that is becoming standard on free
+operating systems. It's also used for Samba documentation.
+
+The SGML files are source code that can be translated into various
+useful output formats, primarily PDF, HTML, Postscript and plain text.
+
+To do this transformation on Debian, you should install the
+docbook-utils package. Having done that, you can say
+
+ docbook2pdf rsync.sgml
+
+and so on.
+
+On other systems you probably need James Clark's "sp" and "JadeTeX"
+packages. Work it out for yourself and send a note to the mailing
+list.
+
diff --git a/rsync/doc/profile.txt b/rsync/doc/profile.txt
new file mode 100644
index 0000000..b911d0a
--- /dev/null
+++ b/rsync/doc/profile.txt
@@ -0,0 +1,42 @@
+Notes on rsync profiling
+
+strlcpy is hot:
+
+ 0.00 0.00 1/7735635 push_dir [68]
+ 0.00 0.00 1/7735635 pop_dir [71]
+ 0.00 0.00 1/7735635 send_file_list [15]
+ 0.01 0.00 18857/7735635 send_files [4]
+ 0.04 0.00 129260/7735635 send_file_entry [18]
+ 0.04 0.00 129260/7735635 make_file [20]
+ 0.04 0.00 141666/7735635 send_directory [36]
+ 2.29 0.00 7316589/7735635 f_name [13]
+[14] 11.7 2.42 0.00 7735635 strlcpy [14]
+
+
+Here's the top few functions:
+
+ 46.23 9.57 9.57 13160929 0.00 0.00 mdfour64
+ 14.78 12.63 3.06 13160929 0.00 0.00 copy64
+ 11.69 15.05 2.42 7735635 0.00 0.00 strlcpy
+ 10.05 17.13 2.08 41438 0.05 0.38 sum_update
+ 4.11 17.98 0.85 13159996 0.00 0.00 mdfour_update
+ 1.50 18.29 0.31 file_compare
+ 1.45 18.59 0.30 129261 0.00 0.01 send_file_entry
+ 1.23 18.84 0.26 2557585 0.00 0.00 f_name
+ 1.11 19.07 0.23 1483750 0.00 0.00 u_strcmp
+ 1.11 19.30 0.23 118129 0.00 0.00 writefd_unbuffered
+ 0.92 19.50 0.19 1085011 0.00 0.00 writefd
+ 0.43 19.59 0.09 156987 0.00 0.00 read_timeout
+ 0.43 19.68 0.09 129261 0.00 0.00 clean_fname
+ 0.39 19.75 0.08 32887 0.00 0.38 matched
+ 0.34 19.82 0.07 1 70.00 16293.92 send_files
+ 0.29 19.89 0.06 129260 0.00 0.00 make_file
+ 0.29 19.95 0.06 75430 0.00 0.00 read_unbuffered
+
+
+
+mdfour could perhaps be made faster:
+
+/* NOTE: This code makes no attempt to be fast! */
+
+There might be an optimized version somewhere that we can borrow.
diff --git a/rsync/doc/rsync.sgml b/rsync/doc/rsync.sgml
new file mode 100644
index 0000000..76a50c2
--- /dev/null
+++ b/rsync/doc/rsync.sgml
@@ -0,0 +1,351 @@
+
+
+
+ rsync
+
+ 1996 -- 2002
+ Martin Pool
+ Andrew Tridgell
+
+
+ Martin
+ Pool
+
+
+
+
+ Introduction
+
+ rsync is a flexible program for efficiently copying files or
+ directory trees.
+
+ rsync has many options to select which files will be copied
+ and how they are to be transferred. It may be used as an
+ alternative to ftp, http, scp or rcp.
+
+ The rsync remote-update protocol allows rsync to transfer just
+ the differences between two sets of files across the network link,
+ using an efficient checksum-search algorithm described in the
+ technical report that accompanies this package.
+
+ Some of the additional features of rsync are:
+
+
+
+
+ support for copying links, devices, owners, groups and
+ permissions
+
+
+
+
+
+ exclude and exclude-from options similar to GNU tar
+
+
+
+
+
+ a CVS exclude mode for ignoring the same files that CVS would ignore
+
+
+
+
+ can use any transparent remote shell, including rsh or ssh
+
+
+
+
+ does not require root privileges
+
+
+
+
+ pipelining of file transfers to minimize latency costs
+
+
+
+
+ support for anonymous or authenticated rsync servers (ideal for
+ mirroring)
+
+
+
+
+
+
+
+
+ Using rsync
+
+
+ Introductory example
+
+
+
+ Probably the most common case of rsync usage is to copy files
+ to or from a remote machine using
+ ssh as a network transport. In
+ this situation rsync is a good alternative to
+ scp.
+
+
+
+ The most commonly used arguments for rsync are
+
+
+
+
+
+
+ Be verbose. Primarily, display the name of each file as it is copied.
+
+
+
+
+
+
+
+
+ Reproduce the structure and attributes of the origin files as exactly
+ as possible: this includes copying subdirectories, symlinks, special
+ files, ownership and permissions. (@xref{Attributes to
+ copy}.)
+
+
+
+
+
+
+
+
+
+
+ Compress network traffic, using a modified version of the
+ @command{zlib} library.
+
+
+ Display a progress indicator while files are transferred. This should
+ normally be ommitted if rsync is not run on a terminal.
+
+
+
+
+
+
+
+ Local and remote
+
+ There are six different ways of using rsync. They
+ are:
+
+
+
+
+
+
+
+ for copying local files. This is invoked when neither
+ source nor destination path contains a @code{:} separator
+
+
+
+ for copying from the local machine to a remote machine using
+ a remote shell program as the transport (such as rsh or
+ ssh). This is invoked when the destination path contains a
+ single @code{:} separator.
+
+
+
+ for copying from a remote machine to the local machine
+ using a remote shell program. This is invoked when the source
+ contains a @code{:} separator.
+
+
+
+ for copying from a remote rsync server to the local
+ machine. This is invoked when the source path contains a @code{::}
+ separator or a @code{rsync://} URL.
+
+
+
+ for copying from the local machine to a remote rsync
+ server. This is invoked when the destination path contains a @code{::}
+ separator.
+
+
+
+ for listing files on a remote machine. This is done the
+ same way as rsync transfers except that you leave off the
+ local destination.
+
+
+
+
+Note that in all cases (other than listing) at least one of the source
+and destination paths must be local.
+
+
+Any one invocation of rsync makes a copy in a single direction. rsync
+currently has no equivalent of @command{ftp}'s interactive mode.
+
+@cindex @sc{nfs}
+@cindex network filesystems
+@cindex remote filesystems
+
+
+rsync's network protocol is generally faster at copying files than
+network filesystems such as @sc{nfs} or @sc{cifs}. It is better to
+run rsync on the file server either as a daemon or over ssh than
+running rsync giving the network directory.
+
+
+
+
+
+
+
+ Frequently asked questions
+
+
+
+
+
+
+
+
+
+ Are there mailing lists for rsync?
+
+
+
+ Yes, and you can subscribe and unsubscribe through a
+ web interface at
+ http://lists.samba.org/
+
+
+
+ If you are having trouble with the mailing list, please
+ send mail to the administrator
+
+ rsync-admin@lists.samba.org
+
+ not to the list itself.
+
+
+
+ The mailing list archives are searchable. Use
+ Google and prepend
+ the search with site:lists.samba.org
+ rsync, plus relevant keywords.
+
+
+
+
+
+
+
+
+ Why is rsync so much bigger when I build it with
+ gcc?
+
+
+
+
+ On gcc, rsync builds by default with debug symbols
+ included. If you strip both executables, they should end
+ up about the same size. (Use make
+ install-strip.)
+
+
+
+
+
+
+
+ Is rsync useful for a single large file like an ISO image?
+
+
+
+ Yes, but note the following:
+
+
+ Background: A common use of rsync is to update a file (or set of files) in one location from a more
+ correct or up-to-date copy in another location, taking advantage of portions of the files that are
+ identical to speed up the process. (Note that rsync will transfer a file in its entirety if no copy
+ exists at the destination.)
+
+
+ (This discussion is written in terms of updating a local copy of a file from a correct file in a
+ remote location, although rsync can work in either direction.)
+
+
+ The file to be updated (the local file) must be in a destination directory that has enough space for
+ two copies of the file. (In addition, keep an extra copy of the file to be updated in a different
+ location for safety -- see the discussion (below) about rsync's behavior when the rsync process is
+ interrupted before completion.)
+
+
+ The local file must have the same name as the remote file being sync'd to (I think?). If you are
+ trying to upgrade an iso from, for example, beta1 to beta2, rename the local file to the same name
+ as the beta2 file. *(This is a useful thing to do -- only the changed portions will be
+ transmitted.)*
+
+
+ The extra copy of the local file kept in a different location is because of rsync's behavior if
+ interrupted before completion:
+
+
+ * If you specify the --partial option and rsync is interrupted, rsync will save the partially
+ rsync'd file and throw away the original local copy. (The partially rsync'd file is correct but
+ truncated.) If rsync is restarted, it will not have a local copy of the file to check for duplicate
+ blocks beyond the section of the file that has already been rsync'd, thus the remainder of the rsync
+ process will be a "pure transfer" of the file rather than taking advantage of the rsync algorithm.
+
+
+ * If you don't specify the --partial option and rsync is interrupted, rsync will throw away the
+ partially rsync'd file, and, when rsync is restarted starts the rsync process over from the
+ beginning.
+
+
+ Which of these is most desirable depends on the degree of commonality between the local and remote
+ copies of the file *and how much progress was made before the interruption*.
+
+
+ The ideal approach after an interruption would be to create a new file by taking the original file
+ and deleting a portion equal in size to the portion already rsync'd and then appending *the
+ remaining* portion to the portion of the file that has already been rsync'd. (There has been some
+ discussion about creating an option to do this automatically.)
+
+ The --compare-dest option is useful when transferring multiple files, but is of no benefit in
+ transferring a single file. (AFAIK)
+
+ *Other potentially useful information can be found at:
+ -[3]http://twiki.org/cgi-bin/view/Wikilearn/RsyncingALargeFile
+
+ This answer, formatted with "real" bullets, can be found at:
+ -[4]http://twiki.org/cgi-bin/view/Wikilearn/RsyncingALargeFileFAQ*
+
+
+
+
+
+
+
+
+
+ Other Resources
+
+
+
+
\ No newline at end of file
diff --git a/rsync/errcode.h b/rsync/errcode.h
new file mode 100644
index 0000000..a428b89
--- /dev/null
+++ b/rsync/errcode.h
@@ -0,0 +1,64 @@
+/*
+ * Error codes returned by rsync.
+ *
+ * Copyright (C) 1998-2000 Andrew Tridgell
+ * Copyright (C) 2003-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+/* If you change these, please also update the string mappings in log.c and
+ * the EXIT VALUES in rsync.yo. */
+
+#define RERR_OK 0
+#define RERR_SYNTAX 1 /* syntax or usage error */
+#define RERR_PROTOCOL 2 /* protocol incompatibility */
+#define RERR_FILESELECT 3 /* errors selecting input/output files, dirs */
+#define RERR_UNSUPPORTED 4 /* requested action not supported */
+#define RERR_STARTCLIENT 5 /* error starting client-server protocol */
+
+#define RERR_SOCKETIO 10 /* error in socket IO */
+#define RERR_FILEIO 11 /* error in file IO */
+#define RERR_STREAMIO 12 /* error in rsync protocol data stream */
+#define RERR_MESSAGEIO 13 /* errors with program diagnostics */
+#define RERR_IPC 14 /* error in IPC code */
+#define RERR_CRASHED 15 /* sibling crashed */
+#define RERR_TERMINATED 16 /* sibling terminated abnormally */
+
+#define RERR_SIGNAL1 19 /* status returned when sent SIGUSR1 */
+#define RERR_SIGNAL 20 /* status returned when sent SIGINT, SIGTERM, SIGHUP */
+#define RERR_WAITCHILD 21 /* some error returned by waitpid() */
+#define RERR_MALLOC 22 /* error allocating core memory buffers */
+#define RERR_PARTIAL 23 /* partial transfer */
+#define RERR_VANISHED 24 /* file(s) vanished on sender side */
+#define RERR_DEL_LIMIT 25 /* skipped some deletes due to --max-delete */
+
+#define RERR_TIMEOUT 30 /* timeout in data send/receive */
+#define RERR_CONTIMEOUT 35 /* timeout waiting for daemon connection */
+
+/* Although it doesn't seem to be specified anywhere,
+ * ssh and the shell seem to return these values:
+ *
+ * 124 if the command exited with status 255
+ * 125 if the command is killed by a signal
+ * 126 if the command cannot be run
+ * 127 if the command is not found
+ *
+ * and we could use this to give a better explanation if the remote
+ * command is not found.
+ */
+#define RERR_CMD_FAILED 124
+#define RERR_CMD_KILLED 125
+#define RERR_CMD_RUN 126
+#define RERR_CMD_NOTFOUND 127
diff --git a/rsync/exclude.c b/rsync/exclude.c
new file mode 100644
index 0000000..efa5b48
--- /dev/null
+++ b/rsync/exclude.c
@@ -0,0 +1,1401 @@
+/*
+ * The filter include/exclude routines.
+ *
+ * Copyright (C) 1996-2001 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2002 Martin Pool
+ * Copyright (C) 2003-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+extern int am_server;
+extern int am_sender;
+extern int eol_nulls;
+extern int io_error;
+extern int local_server;
+extern int prune_empty_dirs;
+extern int ignore_perishable;
+extern int delete_mode;
+extern int delete_excluded;
+extern int cvs_exclude;
+extern int sanitize_paths;
+extern int protocol_version;
+extern int module_id;
+
+extern char curr_dir[MAXPATHLEN];
+extern unsigned int curr_dir_len;
+extern unsigned int module_dirlen;
+
+filter_rule_list filter_list = { .debug_type = "" };
+filter_rule_list cvs_filter_list = { .debug_type = " [global CVS]" };
+filter_rule_list daemon_filter_list = { .debug_type = " [daemon]" };
+
+/* Need room enough for ":MODS " prefix plus some room to grow. */
+#define MAX_RULE_PREFIX (16)
+
+#define SLASH_WILD3_SUFFIX "/***"
+
+/* The dirbuf is set by push_local_filters() to the current subdirectory
+ * relative to curr_dir that is being processed. The path always has a
+ * trailing slash appended, and the variable dirbuf_len contains the length
+ * of this path prefix. The path is always absolute. */
+static char dirbuf[MAXPATHLEN+1];
+static unsigned int dirbuf_len = 0;
+static int dirbuf_depth;
+
+/* This is True when we're scanning parent dirs for per-dir merge-files. */
+static BOOL parent_dirscan = False;
+
+/* This array contains a list of all the currently active per-dir merge
+ * files. This makes it easier to save the appropriate values when we
+ * "push" down into each subdirectory. */
+static filter_rule **mergelist_parents;
+static int mergelist_cnt = 0;
+static int mergelist_size = 0;
+
+/* Each filter_list_struct describes a singly-linked list by keeping track
+ * of both the head and tail pointers. The list is slightly unusual in that
+ * a parent-dir's content can be appended to the end of the local list in a
+ * special way: the last item in the local list has its "next" pointer set
+ * to point to the inherited list, but the local list's tail pointer points
+ * at the end of the local list. Thus, if the local list is empty, the head
+ * will be pointing at the inherited content but the tail will be NULL. To
+ * help you visualize this, here are the possible list arrangements:
+ *
+ * Completely Empty Local Content Only
+ * ================================== ====================================
+ * head -> NULL head -> Local1 -> Local2 -> NULL
+ * tail -> NULL tail -------------^
+ *
+ * Inherited Content Only Both Local and Inherited Content
+ * ================================== ====================================
+ * head -> Parent1 -> Parent2 -> NULL head -> L1 -> L2 -> P1 -> P2 -> NULL
+ * tail -> NULL tail ---------^
+ *
+ * This means that anyone wanting to traverse the whole list to use it just
+ * needs to start at the head and use the "next" pointers until it goes
+ * NULL. To add new local content, we insert the item after the tail item
+ * and update the tail (obviously, if "tail" was NULL, we insert it at the
+ * head). To clear the local list, WE MUST NOT FREE THE INHERITED CONTENT
+ * because it is shared between the current list and our parent list(s).
+ * The easiest way to handle this is to simply truncate the list after the
+ * tail item and then free the local list from the head. When inheriting
+ * the list for a new local dir, we just save off the filter_list_struct
+ * values (so we can pop back to them later) and set the tail to NULL.
+ */
+
+static void teardown_mergelist(filter_rule *ex)
+{
+ if (DEBUG_GTE(FILTER, 2)) {
+ rprintf(FINFO, "[%s] deactivating mergelist #%d%s\n",
+ who_am_i(), mergelist_cnt - 1,
+ ex->u.mergelist->debug_type);
+ }
+
+ /* We should deactivate mergelists in LIFO order. */
+ assert(mergelist_cnt > 0);
+ assert(ex == mergelist_parents[mergelist_cnt - 1]);
+
+ /* The parent_dirscan filters should have been freed. */
+ assert(ex->u.mergelist->parent_dirscan_head == NULL);
+
+ free(ex->u.mergelist->debug_type);
+ free(ex->u.mergelist);
+ mergelist_cnt--;
+}
+
+static void free_filter(filter_rule *ex)
+{
+ free(ex->pattern);
+ free(ex);
+}
+
+static void free_filters(filter_rule *head)
+{
+ filter_rule *rev_head = NULL;
+
+ /* Reverse the list so we deactivate mergelists in the proper LIFO
+ * order. */
+ while (head) {
+ filter_rule *next = head->next;
+ head->next = rev_head;
+ rev_head = head;
+ head = next;
+ }
+
+ while (rev_head) {
+ filter_rule *prev = rev_head->next;
+ /* Tear down mergelists here, not in free_filter, so that we
+ * affect only real filter lists and not temporarily allocated
+ * filters. */
+ if (rev_head->rflags & FILTRULE_PERDIR_MERGE)
+ teardown_mergelist(rev_head);
+ free_filter(rev_head);
+ rev_head = prev;
+ }
+}
+
+/* Build a filter structure given a filter pattern. The value in "pat"
+ * is not null-terminated. "rule" is either held or freed, so the
+ * caller should not free it. */
+static void add_rule(filter_rule_list *listp, const char *pat, unsigned int pat_len,
+ filter_rule *rule, int xflags)
+{
+ const char *cp;
+ unsigned int pre_len, suf_len, slash_cnt = 0;
+
+ if (DEBUG_GTE(FILTER, 2)) {
+ rprintf(FINFO, "[%s] add_rule(%s%.*s%s)%s\n",
+ who_am_i(), get_rule_prefix(rule, pat, 0, NULL),
+ (int)pat_len, pat,
+ (rule->rflags & FILTRULE_DIRECTORY) ? "/" : "",
+ listp->debug_type);
+ }
+
+ /* These flags also indicate that we're reading a list that
+ * needs to be filtered now, not post-filtered later. */
+ if (xflags & (XFLG_ANCHORED2ABS|XFLG_ABS_IF_SLASH)
+ && (rule->rflags & FILTRULES_SIDES)
+ == (am_sender ? FILTRULE_RECEIVER_SIDE : FILTRULE_SENDER_SIDE)) {
+ /* This filter applies only to the other side. Drop it. */
+ free_filter(rule);
+ return;
+ }
+
+ if (pat_len > 1 && pat[pat_len-1] == '/') {
+ pat_len--;
+ rule->rflags |= FILTRULE_DIRECTORY;
+ }
+
+ for (cp = pat; cp < pat + pat_len; cp++) {
+ if (*cp == '/')
+ slash_cnt++;
+ }
+
+ if (!(rule->rflags & (FILTRULE_ABS_PATH | FILTRULE_MERGE_FILE))
+ && ((xflags & (XFLG_ANCHORED2ABS|XFLG_ABS_IF_SLASH) && *pat == '/')
+ || (xflags & XFLG_ABS_IF_SLASH && slash_cnt))) {
+ rule->rflags |= FILTRULE_ABS_PATH;
+ if (*pat == '/')
+ pre_len = dirbuf_len - module_dirlen - 1;
+ else
+ pre_len = 0;
+ } else
+ pre_len = 0;
+
+ /* The daemon wants dir-exclude rules to get an appended "/" + "***". */
+ if (xflags & XFLG_DIR2WILD3
+ && BITS_SETnUNSET(rule->rflags, FILTRULE_DIRECTORY, FILTRULE_INCLUDE)) {
+ rule->rflags &= ~FILTRULE_DIRECTORY;
+ suf_len = sizeof SLASH_WILD3_SUFFIX - 1;
+ } else
+ suf_len = 0;
+
+ if (!(rule->pattern = new_array(char, pre_len + pat_len + suf_len + 1)))
+ out_of_memory("add_rule");
+ if (pre_len) {
+ memcpy(rule->pattern, dirbuf + module_dirlen, pre_len);
+ for (cp = rule->pattern; cp < rule->pattern + pre_len; cp++) {
+ if (*cp == '/')
+ slash_cnt++;
+ }
+ }
+ strlcpy(rule->pattern + pre_len, pat, pat_len + 1);
+ pat_len += pre_len;
+ if (suf_len) {
+ memcpy(rule->pattern + pat_len, SLASH_WILD3_SUFFIX, suf_len+1);
+ pat_len += suf_len;
+ slash_cnt++;
+ }
+
+ if (strpbrk(rule->pattern, "*[?")) {
+ rule->rflags |= FILTRULE_WILD;
+ if ((cp = strstr(rule->pattern, "**")) != NULL) {
+ rule->rflags |= FILTRULE_WILD2;
+ /* If the pattern starts with **, note that. */
+ if (cp == rule->pattern)
+ rule->rflags |= FILTRULE_WILD2_PREFIX;
+ /* If the pattern ends with ***, note that. */
+ if (pat_len >= 3
+ && rule->pattern[pat_len-3] == '*'
+ && rule->pattern[pat_len-2] == '*'
+ && rule->pattern[pat_len-1] == '*')
+ rule->rflags |= FILTRULE_WILD3_SUFFIX;
+ }
+ }
+
+ if (rule->rflags & FILTRULE_PERDIR_MERGE) {
+ filter_rule_list *lp;
+ unsigned int len;
+ int i;
+
+ if ((cp = strrchr(rule->pattern, '/')) != NULL)
+ cp++;
+ else
+ cp = rule->pattern;
+
+ /* If the local merge file was already mentioned, don't
+ * add it again. */
+ for (i = 0; i < mergelist_cnt; i++) {
+ filter_rule *ex = mergelist_parents[i];
+ const char *s = strrchr(ex->pattern, '/');
+ if (s)
+ s++;
+ else
+ s = ex->pattern;
+ len = strlen(s);
+ if (len == pat_len - (cp - rule->pattern) && memcmp(s, cp, len) == 0) {
+ free_filter(rule);
+ return;
+ }
+ }
+
+ if (!(lp = new_array(filter_rule_list, 1)))
+ out_of_memory("add_rule");
+ lp->head = lp->tail = lp->parent_dirscan_head = NULL;
+ if (asprintf(&lp->debug_type, " [per-dir %s]", cp) < 0)
+ out_of_memory("add_rule");
+ rule->u.mergelist = lp;
+
+ if (mergelist_cnt == mergelist_size) {
+ mergelist_size += 5;
+ mergelist_parents = realloc_array(mergelist_parents,
+ filter_rule *,
+ mergelist_size);
+ if (!mergelist_parents)
+ out_of_memory("add_rule");
+ }
+ if (DEBUG_GTE(FILTER, 2)) {
+ rprintf(FINFO, "[%s] activating mergelist #%d%s\n",
+ who_am_i(), mergelist_cnt, lp->debug_type);
+ }
+ mergelist_parents[mergelist_cnt++] = rule;
+ } else
+ rule->u.slash_cnt = slash_cnt;
+
+ if (!listp->tail) {
+ rule->next = listp->head;
+ listp->head = listp->tail = rule;
+ } else {
+ rule->next = listp->tail->next;
+ listp->tail->next = rule;
+ listp->tail = rule;
+ }
+}
+
+static void clear_filter_list(filter_rule_list *listp)
+{
+ if (listp->tail) {
+ /* Truncate any inherited items from the local list. */
+ listp->tail->next = NULL;
+ /* Now free everything that is left. */
+ free_filters(listp->head);
+ }
+
+ listp->head = listp->tail = NULL;
+}
+
+/* This returns an expanded (absolute) filename for the merge-file name if
+ * the name has any slashes in it OR if the parent_dirscan var is True;
+ * otherwise it returns the original merge_file name. If the len_ptr value
+ * is non-NULL the merge_file name is limited by the referenced length
+ * value and will be updated with the length of the resulting name. We
+ * always return a name that is null terminated, even if the merge_file
+ * name was not. */
+static char *parse_merge_name(const char *merge_file, unsigned int *len_ptr,
+ unsigned int prefix_skip)
+{
+ static char buf[MAXPATHLEN];
+ char *fn, tmpbuf[MAXPATHLEN];
+ unsigned int fn_len;
+
+ if (!parent_dirscan && *merge_file != '/') {
+ /* Return the name unchanged it doesn't have any slashes. */
+ if (len_ptr) {
+ const char *p = merge_file + *len_ptr;
+ while (--p > merge_file && *p != '/') {}
+ if (p == merge_file) {
+ strlcpy(buf, merge_file, *len_ptr + 1);
+ return buf;
+ }
+ } else if (strchr(merge_file, '/') == NULL)
+ return (char *)merge_file;
+ }
+
+ fn = *merge_file == '/' ? buf : tmpbuf;
+ if (sanitize_paths) {
+ const char *r = prefix_skip ? "/" : NULL;
+ /* null-terminate the name if it isn't already */
+ if (len_ptr && merge_file[*len_ptr]) {
+ char *to = fn == buf ? tmpbuf : buf;
+ strlcpy(to, merge_file, *len_ptr + 1);
+ merge_file = to;
+ }
+ if (!sanitize_path(fn, merge_file, r, dirbuf_depth, SP_DEFAULT)) {
+ rprintf(FERROR, "merge-file name overflows: %s\n",
+ merge_file);
+ return NULL;
+ }
+ fn_len = strlen(fn);
+ } else {
+ strlcpy(fn, merge_file, len_ptr ? *len_ptr + 1 : MAXPATHLEN);
+ fn_len = clean_fname(fn, CFN_COLLAPSE_DOT_DOT_DIRS);
+ }
+
+ /* If the name isn't in buf yet, it wasn't absolute. */
+ if (fn != buf) {
+ int d_len = dirbuf_len - prefix_skip;
+ if (d_len + fn_len >= MAXPATHLEN) {
+ rprintf(FERROR, "merge-file name overflows: %s\n", fn);
+ return NULL;
+ }
+ memcpy(buf, dirbuf + prefix_skip, d_len);
+ memcpy(buf + d_len, fn, fn_len + 1);
+ fn_len = clean_fname(buf, CFN_COLLAPSE_DOT_DOT_DIRS);
+ }
+
+ if (len_ptr)
+ *len_ptr = fn_len;
+ return buf;
+}
+
+/* Sets the dirbuf and dirbuf_len values. */
+void set_filter_dir(const char *dir, unsigned int dirlen)
+{
+ unsigned int len;
+ if (*dir != '/') {
+ memcpy(dirbuf, curr_dir, curr_dir_len);
+ dirbuf[curr_dir_len] = '/';
+ len = curr_dir_len + 1;
+ if (len + dirlen >= MAXPATHLEN)
+ dirlen = 0;
+ } else
+ len = 0;
+ memcpy(dirbuf + len, dir, dirlen);
+ dirbuf[dirlen + len] = '\0';
+ dirbuf_len = clean_fname(dirbuf, CFN_COLLAPSE_DOT_DOT_DIRS);
+ if (dirbuf_len > 1 && dirbuf[dirbuf_len-1] == '.'
+ && dirbuf[dirbuf_len-2] == '/')
+ dirbuf_len -= 2;
+ if (dirbuf_len != 1)
+ dirbuf[dirbuf_len++] = '/';
+ dirbuf[dirbuf_len] = '\0';
+ if (sanitize_paths)
+ dirbuf_depth = count_dir_elements(dirbuf + module_dirlen);
+}
+
+/* This routine takes a per-dir merge-file entry and finishes its setup.
+ * If the name has a path portion then we check to see if it refers to a
+ * parent directory of the first transfer dir. If it does, we scan all the
+ * dirs from that point through the parent dir of the transfer dir looking
+ * for the per-dir merge-file in each one. */
+static BOOL setup_merge_file(int mergelist_num, filter_rule *ex,
+ filter_rule_list *lp)
+{
+ char buf[MAXPATHLEN];
+ char *x, *y, *pat = ex->pattern;
+ unsigned int len;
+
+ if (!(x = parse_merge_name(pat, NULL, 0)) || *x != '/')
+ return 0;
+
+ if (DEBUG_GTE(FILTER, 2)) {
+ rprintf(FINFO, "[%s] performing parent_dirscan for mergelist #%d%s\n",
+ who_am_i(), mergelist_num, lp->debug_type);
+ }
+ y = strrchr(x, '/');
+ *y = '\0';
+ ex->pattern = strdup(y+1);
+ if (!*x)
+ x = "/";
+ if (*x == '/')
+ strlcpy(buf, x, MAXPATHLEN);
+ else
+ pathjoin(buf, MAXPATHLEN, dirbuf, x);
+
+ len = clean_fname(buf, CFN_COLLAPSE_DOT_DOT_DIRS);
+ if (len != 1 && len < MAXPATHLEN-1) {
+ buf[len++] = '/';
+ buf[len] = '\0';
+ }
+ /* This ensures that the specified dir is a parent of the transfer. */
+ for (x = buf, y = dirbuf; *x && *x == *y; x++, y++) {}
+ if (*x)
+ y += strlen(y); /* nope -- skip the scan */
+
+ parent_dirscan = True;
+ while (*y) {
+ char save[MAXPATHLEN];
+ strlcpy(save, y, MAXPATHLEN);
+ *y = '\0';
+ dirbuf_len = y - dirbuf;
+ strlcpy(x, ex->pattern, MAXPATHLEN - (x - buf));
+ parse_filter_file(lp, buf, ex, XFLG_ANCHORED2ABS);
+ if (ex->rflags & FILTRULE_NO_INHERIT) {
+ /* Free the undesired rules to clean up any per-dir
+ * mergelists they defined. Otherwise pop_local_filters
+ * may crash trying to restore nonexistent state for
+ * those mergelists. */
+ free_filters(lp->head);
+ lp->head = NULL;
+ }
+ lp->tail = NULL;
+ strlcpy(y, save, MAXPATHLEN);
+ while ((*x++ = *y++) != '/') {}
+ }
+ /* Save current head for freeing when the mergelist becomes inactive. */
+ lp->parent_dirscan_head = lp->head;
+ parent_dirscan = False;
+ if (DEBUG_GTE(FILTER, 2)) {
+ rprintf(FINFO, "[%s] completed parent_dirscan for mergelist #%d%s\n",
+ who_am_i(), mergelist_num, lp->debug_type);
+ }
+ free(pat);
+ return 1;
+}
+
+struct local_filter_state {
+ int mergelist_cnt;
+ filter_rule_list mergelists[1];
+};
+
+/* Each time rsync changes to a new directory it call this function to
+ * handle all the per-dir merge-files. The "dir" value is the current path
+ * relative to curr_dir (which might not be null-terminated). We copy it
+ * into dirbuf so that we can easily append a file name on the end. */
+void *push_local_filters(const char *dir, unsigned int dirlen)
+{
+ struct local_filter_state *push;
+ int i;
+
+ set_filter_dir(dir, dirlen);
+ if (DEBUG_GTE(FILTER, 2)) {
+ rprintf(FINFO, "[%s] pushing local filters for %s\n",
+ who_am_i(), dirbuf);
+ }
+
+ if (!mergelist_cnt) {
+ /* No old state to save and no new merge files to push. */
+ return NULL;
+ }
+
+ push = (struct local_filter_state *)new_array(char,
+ sizeof (struct local_filter_state)
+ + (mergelist_cnt-1) * sizeof (filter_rule_list));
+ if (!push)
+ out_of_memory("push_local_filters");
+
+ push->mergelist_cnt = mergelist_cnt;
+ for (i = 0; i < mergelist_cnt; i++) {
+ memcpy(&push->mergelists[i], mergelist_parents[i]->u.mergelist,
+ sizeof (filter_rule_list));
+ }
+
+ /* Note: parse_filter_file() might increase mergelist_cnt, so keep
+ * this loop separate from the above loop. */
+ for (i = 0; i < mergelist_cnt; i++) {
+ filter_rule *ex = mergelist_parents[i];
+ filter_rule_list *lp = ex->u.mergelist;
+
+ if (DEBUG_GTE(FILTER, 2)) {
+ rprintf(FINFO, "[%s] pushing mergelist #%d%s\n",
+ who_am_i(), i, lp->debug_type);
+ }
+
+ lp->tail = NULL; /* Switch any local rules to inherited. */
+ if (ex->rflags & FILTRULE_NO_INHERIT)
+ lp->head = NULL;
+
+ if (ex->rflags & FILTRULE_FINISH_SETUP) {
+ ex->rflags &= ~FILTRULE_FINISH_SETUP;
+ if (setup_merge_file(i, ex, lp))
+ set_filter_dir(dir, dirlen);
+ }
+
+ if (strlcpy(dirbuf + dirbuf_len, ex->pattern,
+ MAXPATHLEN - dirbuf_len) < MAXPATHLEN - dirbuf_len) {
+ parse_filter_file(lp, dirbuf, ex,
+ XFLG_ANCHORED2ABS);
+ } else {
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR,
+ "cannot add local filter rules in long-named directory: %s\n",
+ full_fname(dirbuf));
+ }
+ dirbuf[dirbuf_len] = '\0';
+ }
+
+ return (void*)push;
+}
+
+void pop_local_filters(void *mem)
+{
+ struct local_filter_state *pop = (struct local_filter_state *)mem;
+ int i;
+ int old_mergelist_cnt = pop ? pop->mergelist_cnt : 0;
+
+ if (DEBUG_GTE(FILTER, 2))
+ rprintf(FINFO, "[%s] popping local filters\n", who_am_i());
+
+ for (i = mergelist_cnt; i-- > 0; ) {
+ filter_rule *ex = mergelist_parents[i];
+ filter_rule_list *lp = ex->u.mergelist;
+
+ if (DEBUG_GTE(FILTER, 2)) {
+ rprintf(FINFO, "[%s] popping mergelist #%d%s\n",
+ who_am_i(), i, lp->debug_type);
+ }
+
+ clear_filter_list(lp);
+
+ if (i >= old_mergelist_cnt) {
+ /* This mergelist does not exist in the state to be
+ * restored. Free its parent_dirscan list to clean up
+ * any per-dir mergelists defined there so we don't
+ * crash trying to restore nonexistent state for them
+ * below. (Counterpart to setup_merge_file call in
+ * push_local_filters. Must be done here, not in
+ * free_filter, for LIFO order.) */
+ if (DEBUG_GTE(FILTER, 2)) {
+ rprintf(FINFO, "[%s] freeing parent_dirscan filters of mergelist #%d%s\n",
+ who_am_i(), i, ex->u.mergelist->debug_type);
+ }
+ free_filters(lp->parent_dirscan_head);
+ lp->parent_dirscan_head = NULL;
+ }
+ }
+
+ /* If we cleaned things up properly, the only still-active mergelists
+ * should be those with a state to be restored. */
+ assert(mergelist_cnt == old_mergelist_cnt);
+
+ if (!pop) {
+ /* No state to restore. */
+ return;
+ }
+
+ for (i = 0; i < mergelist_cnt; i++) {
+ memcpy(mergelist_parents[i]->u.mergelist, &pop->mergelists[i],
+ sizeof (filter_rule_list));
+ }
+
+ free(pop);
+}
+
+void change_local_filter_dir(const char *dname, int dlen, int dir_depth)
+{
+ static int cur_depth = -1;
+ static void *filt_array[MAXPATHLEN/2+1];
+
+ if (!dname) {
+ for ( ; cur_depth >= 0; cur_depth--) {
+ if (filt_array[cur_depth]) {
+ pop_local_filters(filt_array[cur_depth]);
+ filt_array[cur_depth] = NULL;
+ }
+ }
+ return;
+ }
+
+ assert(dir_depth < MAXPATHLEN/2+1);
+
+ for ( ; cur_depth >= dir_depth; cur_depth--) {
+ if (filt_array[cur_depth]) {
+ pop_local_filters(filt_array[cur_depth]);
+ filt_array[cur_depth] = NULL;
+ }
+ }
+
+ cur_depth = dir_depth;
+ filt_array[cur_depth] = push_local_filters(dname, dlen);
+}
+
+static int rule_matches(const char *fname, filter_rule *ex, int name_is_dir)
+{
+ int slash_handling, str_cnt = 0, anchored_match = 0;
+ int ret_match = ex->rflags & FILTRULE_NEGATE ? 0 : 1;
+ char *p, *pattern = ex->pattern;
+ const char *strings[16]; /* more than enough */
+ const char *name = fname + (*fname == '/');
+
+ if (!*name)
+ return 0;
+
+ if (!ex->u.slash_cnt && !(ex->rflags & FILTRULE_WILD2)) {
+ /* If the pattern does not have any slashes AND it does
+ * not have a "**" (which could match a slash), then we
+ * just match the name portion of the path. */
+ if ((p = strrchr(name,'/')) != NULL)
+ name = p+1;
+ } else if (ex->rflags & FILTRULE_ABS_PATH && *fname != '/'
+ && curr_dir_len > module_dirlen + 1) {
+ /* If we're matching against an absolute-path pattern,
+ * we need to prepend our full path info. */
+ strings[str_cnt++] = curr_dir + module_dirlen + 1;
+ strings[str_cnt++] = "/";
+ } else if (ex->rflags & FILTRULE_WILD2_PREFIX && *fname != '/') {
+ /* Allow "**"+"/" to match at the start of the string. */
+ strings[str_cnt++] = "/";
+ }
+ strings[str_cnt++] = name;
+ if (name_is_dir) {
+ /* Allow a trailing "/"+"***" to match the directory. */
+ if (ex->rflags & FILTRULE_WILD3_SUFFIX)
+ strings[str_cnt++] = "/";
+ } else if (ex->rflags & FILTRULE_DIRECTORY)
+ return !ret_match;
+ strings[str_cnt] = NULL;
+
+ if (*pattern == '/') {
+ anchored_match = 1;
+ pattern++;
+ }
+
+ if (!anchored_match && ex->u.slash_cnt
+ && !(ex->rflags & FILTRULE_WILD2)) {
+ /* A non-anchored match with an infix slash and no "**"
+ * needs to match the last slash_cnt+1 name elements. */
+ slash_handling = ex->u.slash_cnt + 1;
+ } else if (!anchored_match && !(ex->rflags & FILTRULE_WILD2_PREFIX)
+ && ex->rflags & FILTRULE_WILD2) {
+ /* A non-anchored match with an infix or trailing "**" (but not
+ * a prefixed "**") needs to try matching after every slash. */
+ slash_handling = -1;
+ } else {
+ /* The pattern matches only at the start of the path or name. */
+ slash_handling = 0;
+ }
+
+ if (ex->rflags & FILTRULE_WILD) {
+ if (wildmatch_array(pattern, strings, slash_handling))
+ return ret_match;
+ } else if (str_cnt > 1) {
+ if (litmatch_array(pattern, strings, slash_handling))
+ return ret_match;
+ } else if (anchored_match) {
+ if (strcmp(name, pattern) == 0)
+ return ret_match;
+ } else {
+ int l1 = strlen(name);
+ int l2 = strlen(pattern);
+ if (l2 <= l1 &&
+ strcmp(name+(l1-l2),pattern) == 0 &&
+ (l1==l2 || name[l1-(l2+1)] == '/')) {
+ return ret_match;
+ }
+ }
+
+ return !ret_match;
+}
+
+static void report_filter_result(enum logcode code, char const *name,
+ filter_rule const *ent,
+ int name_is_dir, const char *type)
+{
+ /* If a trailing slash is present to match only directories,
+ * then it is stripped out by add_rule(). So as a special
+ * case we add it back in here. */
+
+ if (DEBUG_GTE(FILTER, 1)) {
+ static char *actions[2][2]
+ = { {"show", "hid"}, {"risk", "protect"} };
+ const char *w = who_am_i();
+ rprintf(code, "[%s] %sing %s %s because of pattern %s%s%s\n",
+ w, actions[*w!='s'][!(ent->rflags & FILTRULE_INCLUDE)],
+ name_is_dir ? "directory" : "file", name, ent->pattern,
+ ent->rflags & FILTRULE_DIRECTORY ? "/" : "", type);
+ }
+}
+
+/* Return -1 if file "name" is defined to be excluded by the specified
+ * exclude list, 1 if it is included, and 0 if it was not matched. */
+int check_filter(filter_rule_list *listp, enum logcode code,
+ const char *name, int name_is_dir)
+{
+ filter_rule *ent;
+
+ for (ent = listp->head; ent; ent = ent->next) {
+ if (ignore_perishable && ent->rflags & FILTRULE_PERISHABLE)
+ continue;
+ if (ent->rflags & FILTRULE_PERDIR_MERGE) {
+ int rc = check_filter(ent->u.mergelist, code, name,
+ name_is_dir);
+ if (rc)
+ return rc;
+ continue;
+ }
+ if (ent->rflags & FILTRULE_CVS_IGNORE) {
+ int rc = check_filter(&cvs_filter_list, code, name,
+ name_is_dir);
+ if (rc)
+ return rc;
+ continue;
+ }
+ if (rule_matches(name, ent, name_is_dir)) {
+ report_filter_result(code, name, ent, name_is_dir,
+ listp->debug_type);
+ return ent->rflags & FILTRULE_INCLUDE ? 1 : -1;
+ }
+ }
+
+ return 0;
+}
+
+#define RULE_STRCMP(s,r) rule_strcmp((s), (r), sizeof (r) - 1)
+
+static const uchar *rule_strcmp(const uchar *str, const char *rule, int rule_len)
+{
+ if (strncmp((char*)str, rule, rule_len) != 0)
+ return NULL;
+ if (isspace(str[rule_len]) || str[rule_len] == '_' || !str[rule_len])
+ return str + rule_len - 1;
+ if (str[rule_len] == ',')
+ return str + rule_len;
+ return NULL;
+}
+
+#define FILTRULES_FROM_CONTAINER (FILTRULE_ABS_PATH | FILTRULE_INCLUDE \
+ | FILTRULE_DIRECTORY | FILTRULE_NEGATE \
+ | FILTRULE_PERISHABLE)
+
+/* Gets the next include/exclude rule from *rulestr_ptr and advances
+ * *rulestr_ptr to point beyond it. Stores the pattern's start (within
+ * *rulestr_ptr) and length in *pat_ptr and *pat_len_ptr, and returns a newly
+ * allocated filter_rule containing the rest of the information. Returns
+ * NULL if there are no more rules in the input.
+ *
+ * The template provides defaults for the new rule to inherit, and the
+ * template rflags and the xflags additionally affect parsing. */
+static filter_rule *parse_rule_tok(const char **rulestr_ptr,
+ const filter_rule *template, int xflags,
+ const char **pat_ptr, unsigned int *pat_len_ptr)
+{
+ const uchar *s = (const uchar *)*rulestr_ptr;
+ filter_rule *rule;
+ unsigned int len;
+
+ if (template->rflags & FILTRULE_WORD_SPLIT) {
+ /* Skip over any initial whitespace. */
+ while (isspace(*s))
+ s++;
+ /* Update to point to real start of rule. */
+ *rulestr_ptr = (const char *)s;
+ }
+ if (!*s)
+ return NULL;
+
+ if (!(rule = new0(filter_rule)))
+ out_of_memory("parse_rule_tok");
+
+ /* Inherit from the template. Don't inherit FILTRULES_SIDES; we check
+ * that later. */
+ rule->rflags = template->rflags & FILTRULES_FROM_CONTAINER;
+
+ /* Figure out what kind of a filter rule "s" is pointing at. Note
+ * that if FILTRULE_NO_PREFIXES is set, the rule is either an include
+ * or an exclude based on the inheritance of the FILTRULE_INCLUDE
+ * flag (above). XFLG_OLD_PREFIXES indicates a compatibility mode
+ * for old include/exclude patterns where just "+ " and "- " are
+ * allowed as optional prefixes. */
+ if (template->rflags & FILTRULE_NO_PREFIXES) {
+ if (*s == '!' && template->rflags & FILTRULE_CVS_IGNORE)
+ rule->rflags |= FILTRULE_CLEAR_LIST; /* Tentative! */
+ } else if (xflags & XFLG_OLD_PREFIXES) {
+ if (*s == '-' && s[1] == ' ') {
+ rule->rflags &= ~FILTRULE_INCLUDE;
+ s += 2;
+ } else if (*s == '+' && s[1] == ' ') {
+ rule->rflags |= FILTRULE_INCLUDE;
+ s += 2;
+ } else if (*s == '!')
+ rule->rflags |= FILTRULE_CLEAR_LIST; /* Tentative! */
+ } else {
+ char ch = 0;
+ BOOL prefix_specifies_side = False;
+ switch (*s) {
+ case 'c':
+ if ((s = RULE_STRCMP(s, "clear")) != NULL)
+ ch = '!';
+ break;
+ case 'd':
+ if ((s = RULE_STRCMP(s, "dir-merge")) != NULL)
+ ch = ':';
+ break;
+ case 'e':
+ if ((s = RULE_STRCMP(s, "exclude")) != NULL)
+ ch = '-';
+ break;
+ case 'h':
+ if ((s = RULE_STRCMP(s, "hide")) != NULL)
+ ch = 'H';
+ break;
+ case 'i':
+ if ((s = RULE_STRCMP(s, "include")) != NULL)
+ ch = '+';
+ break;
+ case 'm':
+ if ((s = RULE_STRCMP(s, "merge")) != NULL)
+ ch = '.';
+ break;
+ case 'p':
+ if ((s = RULE_STRCMP(s, "protect")) != NULL)
+ ch = 'P';
+ break;
+ case 'r':
+ if ((s = RULE_STRCMP(s, "risk")) != NULL)
+ ch = 'R';
+ break;
+ case 's':
+ if ((s = RULE_STRCMP(s, "show")) != NULL)
+ ch = 'S';
+ break;
+ default:
+ ch = *s;
+ if (s[1] == ',')
+ s++;
+ break;
+ }
+ switch (ch) {
+ case ':':
+ rule->rflags |= FILTRULE_PERDIR_MERGE
+ | FILTRULE_FINISH_SETUP;
+ /* FALL THROUGH */
+ case '.':
+ rule->rflags |= FILTRULE_MERGE_FILE;
+ break;
+ case '+':
+ rule->rflags |= FILTRULE_INCLUDE;
+ break;
+ case '-':
+ break;
+ case 'S':
+ rule->rflags |= FILTRULE_INCLUDE;
+ /* FALL THROUGH */
+ case 'H':
+ rule->rflags |= FILTRULE_SENDER_SIDE;
+ prefix_specifies_side = True;
+ break;
+ case 'R':
+ rule->rflags |= FILTRULE_INCLUDE;
+ /* FALL THROUGH */
+ case 'P':
+ rule->rflags |= FILTRULE_RECEIVER_SIDE;
+ prefix_specifies_side = True;
+ break;
+ case '!':
+ rule->rflags |= FILTRULE_CLEAR_LIST;
+ break;
+ default:
+ rprintf(FERROR, "Unknown filter rule: `%s'\n", *rulestr_ptr);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ while (ch != '!' && *++s && *s != ' ' && *s != '_') {
+ if (template->rflags & FILTRULE_WORD_SPLIT && isspace(*s)) {
+ s--;
+ break;
+ }
+ switch (*s) {
+ default:
+ invalid:
+ rprintf(FERROR,
+ "invalid modifier '%c' at position %d in filter rule: %s\n",
+ *s, (int)(s - (const uchar *)*rulestr_ptr), *rulestr_ptr);
+ exit_cleanup(RERR_SYNTAX);
+ case '-':
+ if (!BITS_SETnUNSET(rule->rflags, FILTRULE_MERGE_FILE, FILTRULE_NO_PREFIXES))
+ goto invalid;
+ rule->rflags |= FILTRULE_NO_PREFIXES;
+ break;
+ case '+':
+ if (!BITS_SETnUNSET(rule->rflags, FILTRULE_MERGE_FILE, FILTRULE_NO_PREFIXES))
+ goto invalid;
+ rule->rflags |= FILTRULE_NO_PREFIXES
+ | FILTRULE_INCLUDE;
+ break;
+ case '/':
+ rule->rflags |= FILTRULE_ABS_PATH;
+ break;
+ case '!':
+ /* Negation really goes with the pattern, so it
+ * isn't useful as a merge-file default. */
+ if (rule->rflags & FILTRULE_MERGE_FILE)
+ goto invalid;
+ rule->rflags |= FILTRULE_NEGATE;
+ break;
+ case 'C':
+ if (rule->rflags & FILTRULE_NO_PREFIXES || prefix_specifies_side)
+ goto invalid;
+ rule->rflags |= FILTRULE_NO_PREFIXES
+ | FILTRULE_WORD_SPLIT
+ | FILTRULE_NO_INHERIT
+ | FILTRULE_CVS_IGNORE;
+ break;
+ case 'e':
+ if (!(rule->rflags & FILTRULE_MERGE_FILE))
+ goto invalid;
+ rule->rflags |= FILTRULE_EXCLUDE_SELF;
+ break;
+ case 'n':
+ if (!(rule->rflags & FILTRULE_MERGE_FILE))
+ goto invalid;
+ rule->rflags |= FILTRULE_NO_INHERIT;
+ break;
+ case 'p':
+ rule->rflags |= FILTRULE_PERISHABLE;
+ break;
+ case 'r':
+ if (prefix_specifies_side)
+ goto invalid;
+ rule->rflags |= FILTRULE_RECEIVER_SIDE;
+ break;
+ case 's':
+ if (prefix_specifies_side)
+ goto invalid;
+ rule->rflags |= FILTRULE_SENDER_SIDE;
+ break;
+ case 'w':
+ if (!(rule->rflags & FILTRULE_MERGE_FILE))
+ goto invalid;
+ rule->rflags |= FILTRULE_WORD_SPLIT;
+ break;
+ }
+ }
+ if (*s)
+ s++;
+ }
+ if (template->rflags & FILTRULES_SIDES) {
+ if (rule->rflags & FILTRULES_SIDES) {
+ /* The filter and template both specify side(s). This
+ * is dodgy (and won't work correctly if the template is
+ * a one-sided per-dir merge rule), so reject it. */
+ rprintf(FERROR,
+ "specified-side merge file contains specified-side filter: %s\n",
+ *rulestr_ptr);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ rule->rflags |= template->rflags & FILTRULES_SIDES;
+ }
+
+ if (template->rflags & FILTRULE_WORD_SPLIT) {
+ const uchar *cp = s;
+ /* Token ends at whitespace or the end of the string. */
+ while (!isspace(*cp) && *cp != '\0')
+ cp++;
+ len = cp - s;
+ } else
+ len = strlen((char*)s);
+
+ if (rule->rflags & FILTRULE_CLEAR_LIST) {
+ if (!(rule->rflags & FILTRULE_NO_PREFIXES)
+ && !(xflags & XFLG_OLD_PREFIXES) && len) {
+ rprintf(FERROR,
+ "'!' rule has trailing characters: %s\n", *rulestr_ptr);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (len > 1)
+ rule->rflags &= ~FILTRULE_CLEAR_LIST;
+ } else if (!len && !(rule->rflags & FILTRULE_CVS_IGNORE)) {
+ rprintf(FERROR, "unexpected end of filter rule: %s\n", *rulestr_ptr);
+ exit_cleanup(RERR_SYNTAX);
+ }
+
+ /* --delete-excluded turns an un-modified include/exclude into a sender-side rule. */
+ if (delete_excluded
+ && !(rule->rflags & (FILTRULES_SIDES|FILTRULE_MERGE_FILE|FILTRULE_PERDIR_MERGE)))
+ rule->rflags |= FILTRULE_SENDER_SIDE;
+
+ *pat_ptr = (const char *)s;
+ *pat_len_ptr = len;
+ *rulestr_ptr = *pat_ptr + len;
+ return rule;
+}
+
+static char default_cvsignore[] =
+ /* These default ignored items come from the CVS manual. */
+ "RCS SCCS CVS CVS.adm RCSLOG cvslog.* tags TAGS"
+ " .make.state .nse_depinfo *~ #* .#* ,* _$* *$"
+ " *.old *.bak *.BAK *.orig *.rej .del-*"
+ " *.a *.olb *.o *.obj *.so *.exe"
+ " *.Z *.elc *.ln core"
+ /* The rest we added to suit ourself. */
+ " .svn/ .git/ .hg/ .bzr/";
+
+static void get_cvs_excludes(uint32 rflags)
+{
+ static int initialized = 0;
+ char *p, fname[MAXPATHLEN];
+
+ if (initialized)
+ return;
+ initialized = 1;
+
+ parse_filter_str(&cvs_filter_list, default_cvsignore,
+ rule_template(rflags | (protocol_version >= 30 ? FILTRULE_PERISHABLE : 0)),
+ 0);
+
+ p = module_id >= 0 && lp_use_chroot(module_id) ? "/" : getenv("HOME");
+ if (p && pathjoin(fname, MAXPATHLEN, p, ".cvsignore") < MAXPATHLEN)
+ parse_filter_file(&cvs_filter_list, fname, rule_template(rflags), 0);
+
+ parse_filter_str(&cvs_filter_list, getenv("CVSIGNORE"), rule_template(rflags), 0);
+}
+
+const filter_rule *rule_template(uint32 rflags)
+{
+ static filter_rule template; /* zero-initialized */
+ template.rflags = rflags;
+ return &template;
+}
+
+void parse_filter_str(filter_rule_list *listp, const char *rulestr,
+ const filter_rule *template, int xflags)
+{
+ filter_rule *rule;
+ const char *pat;
+ unsigned int pat_len;
+
+ if (!rulestr)
+ return;
+
+ while (1) {
+ uint32 new_rflags;
+
+ /* Remember that the returned string is NOT '\0' terminated! */
+ if (!(rule = parse_rule_tok(&rulestr, template, xflags, &pat, &pat_len)))
+ break;
+
+ if (pat_len >= MAXPATHLEN) {
+ rprintf(FERROR, "discarding over-long filter: %.*s\n",
+ (int)pat_len, pat);
+ free_continue:
+ free_filter(rule);
+ continue;
+ }
+
+ new_rflags = rule->rflags;
+ if (new_rflags & FILTRULE_CLEAR_LIST) {
+ if (DEBUG_GTE(FILTER, 2)) {
+ rprintf(FINFO,
+ "[%s] clearing filter list%s\n",
+ who_am_i(), listp->debug_type);
+ }
+ clear_filter_list(listp);
+ goto free_continue;
+ }
+
+ if (new_rflags & FILTRULE_MERGE_FILE) {
+ if (!pat_len) {
+ pat = ".cvsignore";
+ pat_len = 10;
+ }
+ if (new_rflags & FILTRULE_EXCLUDE_SELF) {
+ const char *name;
+ filter_rule *excl_self;
+
+ if (!(excl_self = new0(filter_rule)))
+ out_of_memory("parse_filter_str");
+ /* Find the beginning of the basename and add an exclude for it. */
+ for (name = pat + pat_len; name > pat && name[-1] != '/'; name--) {}
+ add_rule(listp, name, (pat + pat_len) - name, excl_self, 0);
+ rule->rflags &= ~FILTRULE_EXCLUDE_SELF;
+ }
+ if (new_rflags & FILTRULE_PERDIR_MERGE) {
+ if (parent_dirscan) {
+ const char *p;
+ unsigned int len = pat_len;
+ if ((p = parse_merge_name(pat, &len, module_dirlen)))
+ add_rule(listp, p, len, rule, 0);
+ else
+ free_filter(rule);
+ continue;
+ }
+ } else {
+ const char *p;
+ unsigned int len = pat_len;
+ if ((p = parse_merge_name(pat, &len, 0)))
+ parse_filter_file(listp, p, rule, XFLG_FATAL_ERRORS);
+ free_filter(rule);
+ continue;
+ }
+ }
+
+ add_rule(listp, pat, pat_len, rule, xflags);
+
+ if (new_rflags & FILTRULE_CVS_IGNORE
+ && !(new_rflags & FILTRULE_MERGE_FILE))
+ get_cvs_excludes(new_rflags);
+ }
+}
+
+void parse_filter_file(filter_rule_list *listp, const char *fname, const filter_rule *template, int xflags)
+{
+ FILE *fp;
+ char line[BIGPATHBUFLEN];
+ char *eob = line + sizeof line - 1;
+ BOOL word_split = (template->rflags & FILTRULE_WORD_SPLIT) != 0;
+
+ if (!fname || !*fname)
+ return;
+
+ if (*fname != '-' || fname[1] || am_server) {
+ if (daemon_filter_list.head) {
+ strlcpy(line, fname, sizeof line);
+ clean_fname(line, CFN_COLLAPSE_DOT_DOT_DIRS);
+ if (check_filter(&daemon_filter_list, FLOG, line, 0) < 0)
+ fp = NULL;
+ else
+ fp = fopen(line, "rb");
+ } else
+ fp = fopen(fname, "rb");
+ } else
+ fp = stdin;
+
+ if (DEBUG_GTE(FILTER, 2)) {
+ rprintf(FINFO, "[%s] parse_filter_file(%s,%x,%x)%s\n",
+ who_am_i(), fname, template->rflags, xflags,
+ fp ? "" : " [not found]");
+ }
+
+ if (!fp) {
+ if (xflags & XFLG_FATAL_ERRORS) {
+ rsyserr(FERROR, errno,
+ "failed to open %sclude file %s",
+ template->rflags & FILTRULE_INCLUDE ? "in" : "ex",
+ fname);
+ exit_cleanup(RERR_FILEIO);
+ }
+ return;
+ }
+ dirbuf[dirbuf_len] = '\0';
+
+ while (1) {
+ char *s = line;
+ int ch, overflow = 0;
+ while (1) {
+ if ((ch = getc(fp)) == EOF) {
+ if (ferror(fp) && errno == EINTR) {
+ clearerr(fp);
+ continue;
+ }
+ break;
+ }
+ if (word_split && isspace(ch))
+ break;
+ if (eol_nulls? !ch : (ch == '\n' || ch == '\r'))
+ break;
+ if (s < eob)
+ *s++ = ch;
+ else
+ overflow = 1;
+ }
+ if (overflow) {
+ rprintf(FERROR, "discarding over-long filter: %s...\n", line);
+ s = line;
+ }
+ *s = '\0';
+ /* Skip an empty token and (when line parsing) comments. */
+ if (*line && (word_split || (*line != ';' && *line != '#')))
+ parse_filter_str(listp, line, template, xflags);
+ if (ch == EOF)
+ break;
+ }
+ fclose(fp);
+}
+
+/* If the "for_xfer" flag is set, the prefix is made compatible with the
+ * current protocol_version (if possible) or a NULL is returned (if not
+ * possible). */
+char *get_rule_prefix(filter_rule *rule, const char *pat, int for_xfer,
+ unsigned int *plen_ptr)
+{
+ static char buf[MAX_RULE_PREFIX+1];
+ char *op = buf;
+ int legal_len = for_xfer && protocol_version < 29 ? 1 : MAX_RULE_PREFIX-1;
+
+ if (rule->rflags & FILTRULE_PERDIR_MERGE) {
+ if (legal_len == 1)
+ return NULL;
+ *op++ = ':';
+ } else if (rule->rflags & FILTRULE_INCLUDE)
+ *op++ = '+';
+ else if (legal_len != 1
+ || ((*pat == '-' || *pat == '+') && pat[1] == ' '))
+ *op++ = '-';
+ else
+ legal_len = 0;
+
+ if (rule->rflags & FILTRULE_ABS_PATH)
+ *op++ = '/';
+ if (rule->rflags & FILTRULE_NEGATE)
+ *op++ = '!';
+ if (rule->rflags & FILTRULE_CVS_IGNORE)
+ *op++ = 'C';
+ else {
+ if (rule->rflags & FILTRULE_NO_INHERIT)
+ *op++ = 'n';
+ if (rule->rflags & FILTRULE_WORD_SPLIT)
+ *op++ = 'w';
+ if (rule->rflags & FILTRULE_NO_PREFIXES) {
+ if (rule->rflags & FILTRULE_INCLUDE)
+ *op++ = '+';
+ else
+ *op++ = '-';
+ }
+ }
+ if (rule->rflags & FILTRULE_EXCLUDE_SELF)
+ *op++ = 'e';
+ if (rule->rflags & FILTRULE_SENDER_SIDE
+ && (!for_xfer || protocol_version >= 29))
+ *op++ = 's';
+ if (rule->rflags & FILTRULE_RECEIVER_SIDE
+ && (!for_xfer || protocol_version >= 29
+ || (delete_excluded && am_sender)))
+ *op++ = 'r';
+ if (rule->rflags & FILTRULE_PERISHABLE) {
+ if (!for_xfer || protocol_version >= 30)
+ *op++ = 'p';
+ else if (am_sender)
+ return NULL;
+ }
+ if (op - buf > legal_len)
+ return NULL;
+ if (legal_len)
+ *op++ = ' ';
+ *op = '\0';
+ if (plen_ptr)
+ *plen_ptr = op - buf;
+ return buf;
+}
+
+static void send_rules(int f_out, filter_rule_list *flp)
+{
+ filter_rule *ent, *prev = NULL;
+
+ for (ent = flp->head; ent; ent = ent->next) {
+ unsigned int len, plen, dlen;
+ int elide = 0;
+ char *p;
+
+ /* Note we need to check delete_excluded here in addition to
+ * the code in parse_rule_tok() because some rules may have
+ * been added before we found the --delete-excluded option.
+ * We must also elide any CVS merge-file rules to avoid a
+ * backward compatibility problem, and we elide any no-prefix
+ * merge files as an optimization (since they can only have
+ * include/exclude rules). */
+ if (ent->rflags & FILTRULE_SENDER_SIDE)
+ elide = am_sender ? 1 : -1;
+ if (ent->rflags & FILTRULE_RECEIVER_SIDE)
+ elide = elide ? 0 : am_sender ? -1 : 1;
+ else if (delete_excluded && !elide
+ && (!(ent->rflags & FILTRULE_PERDIR_MERGE)
+ || ent->rflags & FILTRULE_NO_PREFIXES))
+ elide = am_sender ? 1 : -1;
+ if (elide < 0) {
+ if (prev)
+ prev->next = ent->next;
+ else
+ flp->head = ent->next;
+ } else
+ prev = ent;
+ if (elide > 0)
+ continue;
+ if (ent->rflags & FILTRULE_CVS_IGNORE
+ && !(ent->rflags & FILTRULE_MERGE_FILE)) {
+ int f = am_sender || protocol_version < 29 ? f_out : -2;
+ send_rules(f, &cvs_filter_list);
+ if (f == f_out)
+ continue;
+ }
+ p = get_rule_prefix(ent, ent->pattern, 1, &plen);
+ if (!p) {
+ rprintf(FERROR,
+ "filter rules are too modern for remote rsync.\n");
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ if (f_out < 0)
+ continue;
+ len = strlen(ent->pattern);
+ dlen = ent->rflags & FILTRULE_DIRECTORY ? 1 : 0;
+ if (!(plen + len + dlen))
+ continue;
+ write_int(f_out, plen + len + dlen);
+ if (plen)
+ write_buf(f_out, p, plen);
+ write_buf(f_out, ent->pattern, len);
+ if (dlen)
+ write_byte(f_out, '/');
+ }
+ flp->tail = prev;
+}
+
+/* This is only called by the client. */
+void send_filter_list(int f_out)
+{
+ int receiver_wants_list = prune_empty_dirs
+ || (delete_mode && (!delete_excluded || protocol_version >= 29));
+
+ if (local_server || (am_sender && !receiver_wants_list))
+ f_out = -1;
+ if (cvs_exclude && am_sender) {
+ if (protocol_version >= 29)
+ parse_filter_str(&filter_list, ":C", rule_template(0), 0);
+ parse_filter_str(&filter_list, "-C", rule_template(0), 0);
+ }
+
+ send_rules(f_out, &filter_list);
+
+ if (f_out >= 0)
+ write_int(f_out, 0);
+
+ if (cvs_exclude) {
+ if (!am_sender || protocol_version < 29)
+ parse_filter_str(&filter_list, ":C", rule_template(0), 0);
+ if (!am_sender)
+ parse_filter_str(&filter_list, "-C", rule_template(0), 0);
+ }
+}
+
+/* This is only called by the server. */
+void recv_filter_list(int f_in)
+{
+ char line[BIGPATHBUFLEN];
+ int xflags = protocol_version >= 29 ? 0 : XFLG_OLD_PREFIXES;
+ int receiver_wants_list = prune_empty_dirs
+ || (delete_mode
+ && (!delete_excluded || protocol_version >= 29));
+ unsigned int len;
+
+ if (!local_server && (am_sender || receiver_wants_list)) {
+ while ((len = read_int(f_in)) != 0) {
+ if (len >= sizeof line)
+ overflow_exit("recv_rules");
+ read_sbuf(f_in, line, len);
+ parse_filter_str(&filter_list, line, rule_template(0), xflags);
+ }
+ }
+
+ if (cvs_exclude) {
+ if (local_server || am_sender || protocol_version < 29)
+ parse_filter_str(&filter_list, ":C", rule_template(0), 0);
+ if (local_server || am_sender)
+ parse_filter_str(&filter_list, "-C", rule_template(0), 0);
+ }
+
+ if (local_server) /* filter out any rules that aren't for us. */
+ send_rules(-1, &filter_list);
+}
diff --git a/rsync/fileio.c b/rsync/fileio.c
new file mode 100644
index 0000000..abef46d
--- /dev/null
+++ b/rsync/fileio.c
@@ -0,0 +1,288 @@
+/*
+ * File IO utilities used in rsync.
+ *
+ * Copyright (C) 1998 Andrew Tridgell
+ * Copyright (C) 2002 Martin Pool
+ * Copyright (C) 2004-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "inums.h"
+
+#ifndef ENODATA
+#define ENODATA EAGAIN
+#endif
+
+/* We want all reads to be aligned on 1K boundries. */
+#define ALIGN_BOUNDRY 1024
+/* How far past the boundary is an offset? */
+#define ALIGNED_OVERSHOOT(oft) ((oft) & (ALIGN_BOUNDRY-1))
+/* Round up a length to the next boundary */
+#define ALIGNED_LENGTH(len) ((((len) - 1) | (ALIGN_BOUNDRY-1)) + 1)
+
+extern int sparse_files;
+
+static OFF_T sparse_seek = 0;
+
+int sparse_end(int f, OFF_T size)
+{
+ int ret;
+
+ if (!sparse_seek)
+ return 0;
+
+#ifdef HAVE_FTRUNCATE
+ ret = do_ftruncate(f, size);
+#else
+ if (do_lseek(f, sparse_seek-1, SEEK_CUR) != size-1)
+ ret = -1;
+ else {
+ do {
+ ret = write(f, "", 1);
+ } while (ret < 0 && errno == EINTR);
+
+ ret = ret <= 0 ? -1 : 0;
+ }
+#endif
+
+ sparse_seek = 0;
+
+ return ret;
+}
+
+
+static int write_sparse(int f, char *buf, int len)
+{
+ int l1 = 0, l2 = 0;
+ int ret;
+
+ for (l1 = 0; l1 < len && buf[l1] == 0; l1++) {}
+ for (l2 = 0; l2 < len-l1 && buf[len-(l2+1)] == 0; l2++) {}
+
+ sparse_seek += l1;
+
+ if (l1 == len)
+ return len;
+
+ if (sparse_seek)
+ do_lseek(f, sparse_seek, SEEK_CUR);
+ sparse_seek = l2;
+
+ while ((ret = write(f, buf + l1, len - (l1+l2))) <= 0) {
+ if (ret < 0 && errno == EINTR)
+ continue;
+ sparse_seek = 0;
+ return ret;
+ }
+
+ if (ret != (int)(len - (l1+l2))) {
+ sparse_seek = 0;
+ return l1+ret;
+ }
+
+ return len;
+}
+
+
+static char *wf_writeBuf;
+static size_t wf_writeBufSize;
+static size_t wf_writeBufCnt;
+
+int flush_write_file(int f)
+{
+ int ret = 0;
+ char *bp = wf_writeBuf;
+
+ while (wf_writeBufCnt > 0) {
+ if ((ret = write(f, bp, wf_writeBufCnt)) < 0) {
+ if (errno == EINTR)
+ continue;
+ return ret;
+ }
+ wf_writeBufCnt -= ret;
+ bp += ret;
+ }
+ return ret;
+}
+
+
+/*
+ * write_file does not allow incomplete writes. It loops internally
+ * until len bytes are written or errno is set.
+ */
+int write_file(int f, char *buf, int len)
+{
+ int ret = 0;
+
+ while (len > 0) {
+ int r1;
+ if (sparse_files > 0) {
+ int len1 = MIN(len, SPARSE_WRITE_SIZE);
+ r1 = write_sparse(f, buf, len1);
+ } else {
+ if (!wf_writeBuf) {
+ wf_writeBufSize = WRITE_SIZE * 8;
+ wf_writeBufCnt = 0;
+ wf_writeBuf = new_array(char, wf_writeBufSize);
+ if (!wf_writeBuf)
+ out_of_memory("write_file");
+ }
+ r1 = (int)MIN((size_t)len, wf_writeBufSize - wf_writeBufCnt);
+ if (r1) {
+ memcpy(wf_writeBuf + wf_writeBufCnt, buf, r1);
+ wf_writeBufCnt += r1;
+ }
+ if (wf_writeBufCnt == wf_writeBufSize) {
+ if (flush_write_file(f) < 0)
+ return -1;
+ if (!r1 && len)
+ continue;
+ }
+ }
+ if (r1 <= 0) {
+ if (ret > 0)
+ return ret;
+ return r1;
+ }
+ len -= r1;
+ buf += r1;
+ ret += r1;
+ }
+ return ret;
+}
+
+
+/* This provides functionality somewhat similar to mmap() but using read().
+ * It gives sliding window access to a file. mmap() is not used because of
+ * the possibility of another program (such as a mailer) truncating the
+ * file thus giving us a SIGBUS. */
+struct map_struct *map_file(int fd, OFF_T len, int32 read_size, int32 blk_size)
+{
+ struct map_struct *map;
+
+ if (!(map = new0(struct map_struct)))
+ out_of_memory("map_file");
+
+ if (blk_size && (read_size % blk_size))
+ read_size += blk_size - (read_size % blk_size);
+
+ map->fd = fd;
+ map->file_size = len;
+ map->def_window_size = ALIGNED_LENGTH(read_size);
+
+ return map;
+}
+
+
+/* slide the read window in the file */
+char *map_ptr(struct map_struct *map, OFF_T offset, int32 len)
+{
+ OFF_T window_start, read_start;
+ int32 window_size, read_size, read_offset, align_fudge;
+
+ if (len == 0)
+ return NULL;
+ if (len < 0) {
+ rprintf(FERROR, "invalid len passed to map_ptr: %ld\n",
+ (long)len);
+ exit_cleanup(RERR_FILEIO);
+ }
+
+ /* in most cases the region will already be available */
+ if (offset >= map->p_offset && offset+len <= map->p_offset+map->p_len)
+ return map->p + (offset - map->p_offset);
+
+ /* nope, we are going to have to do a read. Work out our desired window */
+ align_fudge = (int32)ALIGNED_OVERSHOOT(offset);
+ window_start = offset - align_fudge;
+ window_size = map->def_window_size;
+ if (window_start + window_size > map->file_size)
+ window_size = (int32)(map->file_size - window_start);
+ if (window_size < len + align_fudge)
+ window_size = ALIGNED_LENGTH(len + align_fudge);
+
+ /* make sure we have allocated enough memory for the window */
+ if (window_size > map->p_size) {
+ map->p = realloc_array(map->p, char, window_size);
+ if (!map->p)
+ out_of_memory("map_ptr");
+ map->p_size = window_size;
+ }
+
+ /* Now try to avoid re-reading any bytes by reusing any bytes from the previous buffer. */
+ if (window_start >= map->p_offset && window_start < map->p_offset + map->p_len
+ && window_start + window_size >= map->p_offset + map->p_len) {
+ read_start = map->p_offset + map->p_len;
+ read_offset = (int32)(read_start - window_start);
+ read_size = window_size - read_offset;
+ memmove(map->p, map->p + (map->p_len - read_offset), read_offset);
+ } else {
+ read_start = window_start;
+ read_size = window_size;
+ read_offset = 0;
+ }
+
+ if (read_size <= 0) {
+ rprintf(FERROR, "invalid read_size of %ld in map_ptr\n",
+ (long)read_size);
+ exit_cleanup(RERR_FILEIO);
+ }
+
+ if (map->p_fd_offset != read_start) {
+ OFF_T ret = do_lseek(map->fd, read_start, SEEK_SET);
+ if (ret != read_start) {
+ rsyserr(FERROR, errno, "lseek returned %s, not %s",
+ big_num(ret), big_num(read_start));
+ exit_cleanup(RERR_FILEIO);
+ }
+ map->p_fd_offset = read_start;
+ }
+ map->p_offset = window_start;
+ map->p_len = window_size;
+
+ while (read_size > 0) {
+ int32 nread = read(map->fd, map->p + read_offset, read_size);
+ if (nread <= 0) {
+ if (!map->status)
+ map->status = nread ? errno : ENODATA;
+ /* The best we can do is zero the buffer -- the file
+ * has changed mid transfer! */
+ memset(map->p + read_offset, 0, read_size);
+ break;
+ }
+ map->p_fd_offset += nread;
+ read_offset += nread;
+ read_size -= nread;
+ }
+
+ return map->p + align_fudge;
+}
+
+
+int unmap_file(struct map_struct *map)
+{
+ int ret;
+
+ if (map->p) {
+ free(map->p);
+ map->p = NULL;
+ }
+ ret = map->status;
+ memset(map, 0, sizeof map[0]);
+ free(map);
+
+ return ret;
+}
diff --git a/rsync/flist.c b/rsync/flist.c
new file mode 100644
index 0000000..c24672e
--- /dev/null
+++ b/rsync/flist.c
@@ -0,0 +1,3231 @@
+/*
+ * Generate and receive file lists.
+ *
+ * Copyright (C) 1996 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2001, 2002 Martin Pool
+ * Copyright (C) 2002-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "ifuncs.h"
+#include "rounding.h"
+#include "inums.h"
+#include "io.h"
+
+extern int am_root;
+extern int am_server;
+extern int am_daemon;
+extern int am_sender;
+extern int am_generator;
+extern int inc_recurse;
+extern int always_checksum;
+extern int module_id;
+extern int ignore_errors;
+extern int numeric_ids;
+extern int recurse;
+extern int use_qsort;
+extern int xfer_dirs;
+extern int filesfrom_fd;
+extern int one_file_system;
+extern int copy_dirlinks;
+extern int preserve_uid;
+extern int preserve_gid;
+extern int preserve_acls;
+extern int preserve_xattrs;
+extern int preserve_links;
+extern int preserve_hard_links;
+extern int preserve_devices;
+extern int preserve_specials;
+extern int delete_during;
+extern int missing_args;
+extern int eol_nulls;
+extern int relative_paths;
+extern int implied_dirs;
+extern int ignore_perishable;
+extern int non_perishable_cnt;
+extern int prune_empty_dirs;
+extern int copy_links;
+extern int copy_unsafe_links;
+extern int protocol_version;
+extern int sanitize_paths;
+extern int munge_symlinks;
+extern int use_safe_inc_flist;
+extern int need_unsorted_flist;
+extern int sender_symlink_iconv;
+extern int output_needs_newline;
+extern int sender_keeps_checksum;
+extern int unsort_ndx;
+extern uid_t our_uid;
+extern struct stats stats;
+extern char *filesfrom_host;
+extern char *usermap, *groupmap;
+
+extern char curr_dir[MAXPATHLEN];
+
+extern struct chmod_mode_struct *chmod_modes;
+
+extern filter_rule_list filter_list;
+extern filter_rule_list daemon_filter_list;
+
+#ifdef ICONV_OPTION
+extern int filesfrom_convert;
+extern iconv_t ic_send, ic_recv;
+#endif
+
+#define PTR_SIZE (sizeof (struct file_struct *))
+
+int io_error;
+int checksum_len;
+dev_t filesystem_dev; /* used to implement -x */
+
+struct file_list *cur_flist, *first_flist, *dir_flist;
+int send_dir_ndx = -1, send_dir_depth = -1;
+int flist_cnt = 0; /* how many (non-tmp) file list objects exist */
+int file_total = 0; /* total of all active items over all file-lists */
+int file_old_total = 0; /* total of active items that will soon be gone */
+int flist_eof = 0; /* all the file-lists are now known */
+
+#define NORMAL_NAME 0
+#define SLASH_ENDING_NAME 1
+#define DOTDIR_NAME 2
+#define MISSING_NAME 3
+
+/* Starting from protocol version 26, we always use 64-bit ino_t and dev_t
+ * internally, even if this platform does not allow files to have 64-bit inums.
+ * The only exception is if we're on a platform with no 64-bit type at all.
+ *
+ * Because we use read_longint() to get these off the wire, if you transfer
+ * devices or (for protocols < 30) hardlinks with dev or inum > 2**32 to a
+ * machine with no 64-bit types then you will get an overflow error.
+ *
+ * Note that if you transfer devices from a 64-bit-devt machine (say, Solaris)
+ * to a 32-bit-devt machine (say, Linux-2.2/x86) then the device numbers will
+ * be truncated. But it's a kind of silly thing to do anyhow. */
+
+/* The tmp_* vars are used as a cache area by make_file() to store data
+ * that the sender doesn't need to remember in its file list. The data
+ * will survive just long enough to be used by send_file_entry(). */
+static dev_t tmp_rdev;
+#ifdef SUPPORT_HARD_LINKS
+static int64 tmp_dev = -1, tmp_ino;
+#endif
+static char tmp_sum[MAX_DIGEST_LEN];
+
+static char empty_sum[MAX_DIGEST_LEN];
+static int flist_count_offset; /* for --delete --progress */
+
+static void flist_sort_and_clean(struct file_list *flist, int strip_root);
+static void output_flist(struct file_list *flist);
+
+void init_flist(void)
+{
+ if (DEBUG_GTE(FLIST, 4)) {
+ rprintf(FINFO, "FILE_STRUCT_LEN=%d, EXTRA_LEN=%d\n",
+ (int)FILE_STRUCT_LEN, (int)EXTRA_LEN);
+ }
+ checksum_len = protocol_version < 21 ? 2
+ : protocol_version < 30 ? MD4_DIGEST_LEN
+ : MD5_DIGEST_LEN;
+}
+
+static int show_filelist_p(void)
+{
+ return INFO_GTE(FLIST, 1) && xfer_dirs && !am_server && !inc_recurse;
+}
+
+static void start_filelist_progress(char *kind)
+{
+ rprintf(FCLIENT, "%s ... ", kind);
+ output_needs_newline = 1;
+ rflush(FINFO);
+}
+
+static void emit_filelist_progress(int count)
+{
+ rprintf(FCLIENT, " %d files...\r", count);
+}
+
+static void maybe_emit_filelist_progress(int count)
+{
+ if (INFO_GTE(FLIST, 2) && show_filelist_p() && (count % 100) == 0)
+ emit_filelist_progress(count);
+}
+
+static void finish_filelist_progress(const struct file_list *flist)
+{
+ if (INFO_GTE(FLIST, 2)) {
+ /* This overwrites the progress line */
+ rprintf(FINFO, "%d file%sto consider\n",
+ flist->used, flist->used == 1 ? " " : "s ");
+ } else {
+ output_needs_newline = 0;
+ rprintf(FINFO, "done\n");
+ }
+}
+
+void show_flist_stats(void)
+{
+ /* Nothing yet */
+}
+
+/* Stat either a symlink or its referent, depending on the settings of
+ * copy_links, copy_unsafe_links, etc. Returns -1 on error, 0 on success.
+ *
+ * If path is the name of a symlink, then the linkbuf buffer (which must hold
+ * MAXPATHLEN chars) will be set to the symlink's target string.
+ *
+ * The stat structure pointed to by stp will contain information about the
+ * link or the referent as appropriate, if they exist. */
+static int readlink_stat(const char *path, STRUCT_STAT *stp, char *linkbuf)
+{
+#ifdef SUPPORT_LINKS
+ if (link_stat(path, stp, copy_dirlinks) < 0)
+ return -1;
+ if (S_ISLNK(stp->st_mode)) {
+ int llen = do_readlink(path, linkbuf, MAXPATHLEN - 1);
+ if (llen < 0)
+ return -1;
+ linkbuf[llen] = '\0';
+ if (copy_unsafe_links && unsafe_symlink(linkbuf, path)) {
+ if (INFO_GTE(SYMSAFE, 1)) {
+ rprintf(FINFO,"copying unsafe symlink \"%s\" -> \"%s\"\n",
+ path, linkbuf);
+ }
+ return x_stat(path, stp, NULL);
+ }
+ if (munge_symlinks && am_sender && llen > SYMLINK_PREFIX_LEN
+ && strncmp(linkbuf, SYMLINK_PREFIX, SYMLINK_PREFIX_LEN) == 0) {
+ memmove(linkbuf, linkbuf + SYMLINK_PREFIX_LEN,
+ llen - SYMLINK_PREFIX_LEN + 1);
+ }
+ }
+ return 0;
+#else
+ return x_stat(path, stp, NULL);
+#endif
+}
+
+int link_stat(const char *path, STRUCT_STAT *stp, int follow_dirlinks)
+{
+#ifdef SUPPORT_LINKS
+ if (copy_links)
+ return x_stat(path, stp, NULL);
+ if (x_lstat(path, stp, NULL) < 0)
+ return -1;
+ if (follow_dirlinks && S_ISLNK(stp->st_mode)) {
+ STRUCT_STAT st;
+ if (x_stat(path, &st, NULL) == 0 && S_ISDIR(st.st_mode))
+ *stp = st;
+ }
+ return 0;
+#else
+ return x_stat(path, stp, NULL);
+#endif
+}
+
+static inline int is_daemon_excluded(const char *fname, int is_dir)
+{
+ if (daemon_filter_list.head
+ && check_filter(&daemon_filter_list, FLOG, fname, is_dir) < 0) {
+ errno = ENOENT;
+ return 1;
+ }
+ return 0;
+}
+
+static inline int path_is_daemon_excluded(char *path, int ignore_filename)
+{
+ if (daemon_filter_list.head) {
+ char *slash = path;
+
+ while ((slash = strchr(slash+1, '/')) != NULL) {
+ int ret;
+ *slash = '\0';
+ ret = check_filter(&daemon_filter_list, FLOG, path, 1);
+ *slash = '/';
+ if (ret < 0) {
+ errno = ENOENT;
+ return 1;
+ }
+ }
+
+ if (!ignore_filename
+ && check_filter(&daemon_filter_list, FLOG, path, 1) < 0) {
+ errno = ENOENT;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* This function is used to check if a file should be included/excluded
+ * from the list of files based on its name and type etc. The value of
+ * filter_level is set to either SERVER_FILTERS or ALL_FILTERS. */
+static int is_excluded(const char *fname, int is_dir, int filter_level)
+{
+#if 0 /* This currently never happens, so avoid a useless compare. */
+ if (filter_level == NO_FILTERS)
+ return 0;
+#endif
+ if (is_daemon_excluded(fname, is_dir))
+ return 1;
+ if (filter_level != ALL_FILTERS)
+ return 0;
+ if (filter_list.head
+ && check_filter(&filter_list, FINFO, fname, is_dir) < 0)
+ return 1;
+ return 0;
+}
+
+static void send_directory(int f, struct file_list *flist,
+ char *fbuf, int len, int flags);
+
+static const char *pathname, *orig_dir;
+static int pathname_len;
+
+/* Make sure flist can hold at least flist->used + extra entries. */
+static void flist_expand(struct file_list *flist, int extra)
+{
+ struct file_struct **new_ptr;
+
+ if (flist->used + extra <= flist->malloced)
+ return;
+
+ if (flist->malloced < FLIST_START)
+ flist->malloced = FLIST_START;
+ else if (flist->malloced >= FLIST_LINEAR)
+ flist->malloced += FLIST_LINEAR;
+ else
+ flist->malloced *= 2;
+
+ /* In case count jumped or we are starting the list
+ * with a known size just set it. */
+ if (flist->malloced < flist->used + extra)
+ flist->malloced = flist->used + extra;
+
+ new_ptr = realloc_array(flist->files, struct file_struct *,
+ flist->malloced);
+
+ if (DEBUG_GTE(FLIST, 1) && flist->malloced != FLIST_START) {
+ rprintf(FCLIENT, "[%s] expand file_list pointer array to %s bytes, did%s move\n",
+ who_am_i(),
+ big_num(sizeof flist->files[0] * flist->malloced),
+ (new_ptr == flist->files) ? " not" : "");
+ }
+
+ flist->files = new_ptr;
+
+ if (!flist->files)
+ out_of_memory("flist_expand");
+}
+
+static void flist_done_allocating(struct file_list *flist)
+{
+ void *ptr = pool_boundary(flist->file_pool, 8*1024);
+ if (flist->pool_boundary == ptr)
+ flist->pool_boundary = NULL; /* list didn't use any pool memory */
+ else
+ flist->pool_boundary = ptr;
+}
+
+/* Call this with EITHER (1) "file, NULL, 0" to chdir() to the file's
+ * F_PATHNAME(), or (2) "NULL, dir, dirlen" to chdir() to the supplied dir,
+ * with dir == NULL taken to be the starting directory, and dirlen < 0
+ * indicating that strdup(dir) should be called and then the -dirlen length
+ * value checked to ensure that it is not daemon-excluded. */
+int change_pathname(struct file_struct *file, const char *dir, int dirlen)
+{
+ if (dirlen < 0) {
+ char *cpy = strdup(dir);
+ if (*cpy != '/')
+ change_dir(orig_dir, CD_SKIP_CHDIR);
+ if (path_is_daemon_excluded(cpy, 0))
+ goto chdir_error;
+ dir = cpy;
+ dirlen = -dirlen;
+ } else {
+ if (file) {
+ if (pathname == F_PATHNAME(file))
+ return 1;
+ dir = F_PATHNAME(file);
+ if (dir)
+ dirlen = strlen(dir);
+ } else if (pathname == dir)
+ return 1;
+ if (dir && *dir != '/')
+ change_dir(orig_dir, CD_SKIP_CHDIR);
+ }
+
+ pathname = dir;
+ pathname_len = dirlen;
+
+ if (!dir)
+ dir = orig_dir;
+
+ if (!change_dir(dir, CD_NORMAL)) {
+ chdir_error:
+ io_error |= IOERR_GENERAL;
+ rsyserr(FERROR_XFER, errno, "change_dir %s failed", full_fname(dir));
+ if (dir != orig_dir)
+ change_dir(orig_dir, CD_NORMAL);
+ pathname = NULL;
+ pathname_len = 0;
+ return 0;
+ }
+
+ return 1;
+}
+
+static void send_file_entry(int f, const char *fname, struct file_struct *file,
+#ifdef SUPPORT_LINKS
+ const char *symlink_name, int symlink_len,
+#endif
+ int ndx, int first_ndx)
+{
+ static time_t modtime;
+ static mode_t mode;
+#ifdef SUPPORT_HARD_LINKS
+ static int64 dev;
+#endif
+ static dev_t rdev;
+ static uint32 rdev_major;
+ static uid_t uid;
+ static gid_t gid;
+ static const char *user_name, *group_name;
+ static char lastname[MAXPATHLEN];
+#ifdef SUPPORT_HARD_LINKS
+ int first_hlink_ndx = -1;
+#endif
+ int l1, l2;
+ int xflags;
+
+ /* Initialize starting value of xflags and adjust counts. */
+ if (S_ISREG(file->mode))
+ xflags = 0;
+ else if (S_ISDIR(file->mode)) {
+ stats.num_dirs++;
+ if (protocol_version >= 30) {
+ if (file->flags & FLAG_CONTENT_DIR)
+ xflags = file->flags & FLAG_TOP_DIR;
+ else if (file->flags & FLAG_IMPLIED_DIR)
+ xflags = XMIT_TOP_DIR | XMIT_NO_CONTENT_DIR;
+ else
+ xflags = XMIT_NO_CONTENT_DIR;
+ } else
+ xflags = file->flags & FLAG_TOP_DIR; /* FLAG_TOP_DIR == XMIT_TOP_DIR */
+ } else {
+ if (S_ISLNK(file->mode))
+ stats.num_symlinks++;
+ else if (IS_DEVICE(file->mode))
+ stats.num_devices++;
+ else if (IS_SPECIAL(file->mode))
+ stats.num_specials++;
+ xflags = 0;
+ }
+
+ if (file->mode == mode)
+ xflags |= XMIT_SAME_MODE;
+ else
+ mode = file->mode;
+
+ if (preserve_devices && IS_DEVICE(mode)) {
+ if (protocol_version < 28) {
+ if (tmp_rdev == rdev)
+ xflags |= XMIT_SAME_RDEV_pre28;
+ else
+ rdev = tmp_rdev;
+ } else {
+ rdev = tmp_rdev;
+ if ((uint32)major(rdev) == rdev_major)
+ xflags |= XMIT_SAME_RDEV_MAJOR;
+ else
+ rdev_major = major(rdev);
+ if (protocol_version < 30 && (uint32)minor(rdev) <= 0xFFu)
+ xflags |= XMIT_RDEV_MINOR_8_pre30;
+ }
+ } else if (preserve_specials && IS_SPECIAL(mode) && protocol_version < 31) {
+ /* Special files don't need an rdev number, so just make
+ * the historical transmission of the value efficient. */
+ if (protocol_version < 28)
+ xflags |= XMIT_SAME_RDEV_pre28;
+ else {
+ rdev = MAKEDEV(major(rdev), 0);
+ xflags |= XMIT_SAME_RDEV_MAJOR;
+ if (protocol_version < 30)
+ xflags |= XMIT_RDEV_MINOR_8_pre30;
+ }
+ } else if (protocol_version < 28)
+ rdev = MAKEDEV(0, 0);
+ if (!preserve_uid || ((uid_t)F_OWNER(file) == uid && *lastname))
+ xflags |= XMIT_SAME_UID;
+ else {
+ uid = F_OWNER(file);
+ if (!numeric_ids) {
+ user_name = add_uid(uid);
+ if (inc_recurse && user_name)
+ xflags |= XMIT_USER_NAME_FOLLOWS;
+ }
+ }
+ if (!preserve_gid || ((gid_t)F_GROUP(file) == gid && *lastname))
+ xflags |= XMIT_SAME_GID;
+ else {
+ gid = F_GROUP(file);
+ if (!numeric_ids) {
+ group_name = add_gid(gid);
+ if (inc_recurse && group_name)
+ xflags |= XMIT_GROUP_NAME_FOLLOWS;
+ }
+ }
+ if (file->modtime == modtime)
+ xflags |= XMIT_SAME_TIME;
+ else
+ modtime = file->modtime;
+ if (NSEC_BUMP(file) && protocol_version >= 31)
+ xflags |= XMIT_MOD_NSEC;
+
+#ifdef SUPPORT_HARD_LINKS
+ if (tmp_dev != -1) {
+ if (protocol_version >= 30) {
+ struct ht_int64_node *np = idev_find(tmp_dev, tmp_ino);
+ first_hlink_ndx = (int32)(long)np->data - 1;
+ if (first_hlink_ndx < 0) {
+ np->data = (void*)(long)(first_ndx + ndx + 1);
+ xflags |= XMIT_HLINK_FIRST;
+ }
+ if (DEBUG_GTE(HLINK, 1)) {
+ if (first_hlink_ndx >= 0) {
+ rprintf(FINFO, "[%s] #%d hard-links #%d (%sabbrev)\n",
+ who_am_i(), first_ndx + ndx, first_hlink_ndx,
+ first_hlink_ndx >= first_ndx ? "" : "un");
+ } else if (DEBUG_GTE(HLINK, 3)) {
+ rprintf(FINFO, "[%s] dev:inode for #%d is %s:%s\n",
+ who_am_i(), first_ndx + ndx,
+ big_num(tmp_dev), big_num(tmp_ino));
+ }
+ }
+ } else {
+ if (tmp_dev == dev) {
+ if (protocol_version >= 28)
+ xflags |= XMIT_SAME_DEV_pre30;
+ } else
+ dev = tmp_dev;
+ }
+ xflags |= XMIT_HLINKED;
+ }
+#endif
+
+ for (l1 = 0;
+ lastname[l1] && (fname[l1] == lastname[l1]) && (l1 < 255);
+ l1++) {}
+ l2 = strlen(fname+l1);
+
+ if (l1 > 0)
+ xflags |= XMIT_SAME_NAME;
+ if (l2 > 255)
+ xflags |= XMIT_LONG_NAME;
+
+ /* We must make sure we don't send a zero flag byte or the
+ * other end will terminate the flist transfer. Note that
+ * the use of XMIT_TOP_DIR on a non-dir has no meaning, so
+ * it's harmless way to add a bit to the first flag byte. */
+ if (protocol_version >= 28) {
+ if (!xflags && !S_ISDIR(mode))
+ xflags |= XMIT_TOP_DIR;
+ if ((xflags & 0xFF00) || !xflags) {
+ xflags |= XMIT_EXTENDED_FLAGS;
+ write_shortint(f, xflags);
+ } else
+ write_byte(f, xflags);
+ } else {
+ if (!(xflags & 0xFF))
+ xflags |= S_ISDIR(mode) ? XMIT_LONG_NAME : XMIT_TOP_DIR;
+ write_byte(f, xflags);
+ }
+ if (xflags & XMIT_SAME_NAME)
+ write_byte(f, l1);
+ if (xflags & XMIT_LONG_NAME)
+ write_varint30(f, l2);
+ else
+ write_byte(f, l2);
+ write_buf(f, fname + l1, l2);
+
+#ifdef SUPPORT_HARD_LINKS
+ if (first_hlink_ndx >= 0) {
+ write_varint(f, first_hlink_ndx);
+ if (first_hlink_ndx >= first_ndx)
+ goto the_end;
+ }
+#endif
+
+ write_varlong30(f, F_LENGTH(file), 3);
+ if (!(xflags & XMIT_SAME_TIME)) {
+ if (protocol_version >= 30)
+ write_varlong(f, modtime, 4);
+ else
+ write_int(f, modtime);
+ }
+ if (xflags & XMIT_MOD_NSEC)
+ write_varint(f, F_MOD_NSEC(file));
+ if (!(xflags & XMIT_SAME_MODE))
+ write_int(f, to_wire_mode(mode));
+ if (preserve_uid && !(xflags & XMIT_SAME_UID)) {
+ if (protocol_version < 30)
+ write_int(f, uid);
+ else {
+ write_varint(f, uid);
+ if (xflags & XMIT_USER_NAME_FOLLOWS) {
+ int len = strlen(user_name);
+ write_byte(f, len);
+ write_buf(f, user_name, len);
+ }
+ }
+ }
+ if (preserve_gid && !(xflags & XMIT_SAME_GID)) {
+ if (protocol_version < 30)
+ write_int(f, gid);
+ else {
+ write_varint(f, gid);
+ if (xflags & XMIT_GROUP_NAME_FOLLOWS) {
+ int len = strlen(group_name);
+ write_byte(f, len);
+ write_buf(f, group_name, len);
+ }
+ }
+ }
+ if ((preserve_devices && IS_DEVICE(mode))
+ || (preserve_specials && IS_SPECIAL(mode) && protocol_version < 31)) {
+ if (protocol_version < 28) {
+ if (!(xflags & XMIT_SAME_RDEV_pre28))
+ write_int(f, (int)rdev);
+ } else {
+ if (!(xflags & XMIT_SAME_RDEV_MAJOR))
+ write_varint30(f, major(rdev));
+ if (protocol_version >= 30)
+ write_varint(f, minor(rdev));
+ else if (xflags & XMIT_RDEV_MINOR_8_pre30)
+ write_byte(f, minor(rdev));
+ else
+ write_int(f, minor(rdev));
+ }
+ }
+
+#ifdef SUPPORT_LINKS
+ if (symlink_len) {
+ write_varint30(f, symlink_len);
+ write_buf(f, symlink_name, symlink_len);
+ }
+#endif
+
+#ifdef SUPPORT_HARD_LINKS
+ if (tmp_dev != -1 && protocol_version < 30) {
+ /* Older protocols expect the dev number to be transmitted
+ * 1-incremented so that it is never zero. */
+ if (protocol_version < 26) {
+ /* 32-bit dev_t and ino_t */
+ write_int(f, (int32)(dev+1));
+ write_int(f, (int32)tmp_ino);
+ } else {
+ /* 64-bit dev_t and ino_t */
+ if (!(xflags & XMIT_SAME_DEV_pre30))
+ write_longint(f, dev+1);
+ write_longint(f, tmp_ino);
+ }
+ }
+#endif
+
+ if (always_checksum && (S_ISREG(mode) || protocol_version < 28)) {
+ const char *sum;
+ if (S_ISREG(mode))
+ sum = tmp_sum;
+ else {
+ /* Prior to 28, we sent a useless set of nulls. */
+ sum = empty_sum;
+ }
+ write_buf(f, sum, checksum_len);
+ }
+
+#ifdef SUPPORT_HARD_LINKS
+ the_end:
+#endif
+ strlcpy(lastname, fname, MAXPATHLEN);
+
+ if (S_ISREG(mode) || S_ISLNK(mode))
+ stats.total_size += F_LENGTH(file);
+}
+
+static struct file_struct *recv_file_entry(int f, struct file_list *flist, int xflags)
+{
+ static int64 modtime;
+ static mode_t mode;
+#ifdef SUPPORT_HARD_LINKS
+ static int64 dev;
+#endif
+ static dev_t rdev;
+ static uint32 rdev_major;
+ static uid_t uid;
+ static gid_t gid;
+ static uint16 gid_flags;
+ static char lastname[MAXPATHLEN], *lastdir;
+ static int lastdir_depth, lastdir_len = -1;
+ static unsigned int del_hier_name_len = 0;
+ static int in_del_hier = 0;
+ char thisname[MAXPATHLEN];
+ unsigned int l1 = 0, l2 = 0;
+ int alloc_len, basename_len, linkname_len;
+ int extra_len = file_extra_cnt * EXTRA_LEN;
+ int first_hlink_ndx = -1;
+ int64 file_length;
+ uint32 modtime_nsec;
+ const char *basename;
+ struct file_struct *file;
+ alloc_pool_t *pool;
+ char *bp;
+
+ if (xflags & XMIT_SAME_NAME)
+ l1 = read_byte(f);
+
+ if (xflags & XMIT_LONG_NAME)
+ l2 = read_varint30(f);
+ else
+ l2 = read_byte(f);
+
+ if (l2 >= MAXPATHLEN - l1) {
+ rprintf(FERROR,
+ "overflow: xflags=0x%x l1=%d l2=%d lastname=%s [%s]\n",
+ xflags, l1, l2, lastname, who_am_i());
+ overflow_exit("recv_file_entry");
+ }
+
+ strlcpy(thisname, lastname, l1 + 1);
+ read_sbuf(f, &thisname[l1], l2);
+ thisname[l1 + l2] = 0;
+
+ /* Abuse basename_len for a moment... */
+ basename_len = strlcpy(lastname, thisname, MAXPATHLEN);
+
+#ifdef ICONV_OPTION
+ if (ic_recv != (iconv_t)-1) {
+ xbuf outbuf, inbuf;
+
+ INIT_CONST_XBUF(outbuf, thisname);
+ INIT_XBUF(inbuf, lastname, basename_len, (size_t)-1);
+
+ if (iconvbufs(ic_recv, &inbuf, &outbuf, ICB_INIT) < 0) {
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR_UTF8,
+ "[%s] cannot convert filename: %s (%s)\n",
+ who_am_i(), lastname, strerror(errno));
+ outbuf.len = 0;
+ }
+ thisname[outbuf.len] = '\0';
+ }
+#endif
+
+ if (*thisname
+ && (clean_fname(thisname, CFN_REFUSE_DOT_DOT_DIRS) < 0 || (!relative_paths && *thisname == '/'))) {
+ rprintf(FERROR, "ABORTING due to unsafe pathname from sender: %s\n", thisname);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ if (sanitize_paths)
+ sanitize_path(thisname, thisname, "", 0, SP_DEFAULT);
+
+ if ((basename = strrchr(thisname, '/')) != NULL) {
+ int len = basename++ - thisname;
+ if (len != lastdir_len || memcmp(thisname, lastdir, len) != 0) {
+ lastdir = new_array(char, len + 1);
+ memcpy(lastdir, thisname, len);
+ lastdir[len] = '\0';
+ lastdir_len = len;
+ lastdir_depth = count_dir_elements(lastdir);
+ }
+ } else
+ basename = thisname;
+ basename_len = strlen(basename) + 1; /* count the '\0' */
+
+#ifdef SUPPORT_HARD_LINKS
+ if (protocol_version >= 30
+ && BITS_SETnUNSET(xflags, XMIT_HLINKED, XMIT_HLINK_FIRST)) {
+ first_hlink_ndx = read_varint(f);
+ if (first_hlink_ndx < 0 || first_hlink_ndx >= flist->ndx_start + flist->used) {
+ rprintf(FERROR,
+ "hard-link reference out of range: %d (%d)\n",
+ first_hlink_ndx, flist->ndx_start + flist->used);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ if (DEBUG_GTE(HLINK, 1)) {
+ rprintf(FINFO, "[%s] #%d hard-links #%d (%sabbrev)\n",
+ who_am_i(), flist->used+flist->ndx_start, first_hlink_ndx,
+ first_hlink_ndx >= flist->ndx_start ? "" : "un");
+ }
+ if (first_hlink_ndx >= flist->ndx_start) {
+ struct file_struct *first = flist->files[first_hlink_ndx - flist->ndx_start];
+ file_length = F_LENGTH(first);
+ modtime = first->modtime;
+ modtime_nsec = F_MOD_NSEC(first);
+ mode = first->mode;
+ if (preserve_uid)
+ uid = F_OWNER(first);
+ if (preserve_gid)
+ gid = F_GROUP(first);
+ if (preserve_devices && IS_DEVICE(mode)) {
+ uint32 *devp = F_RDEV_P(first);
+ rdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
+ extra_len += DEV_EXTRA_CNT * EXTRA_LEN;
+ }
+ if (preserve_links && S_ISLNK(mode))
+ linkname_len = strlen(F_SYMLINK(first)) + 1;
+ else
+ linkname_len = 0;
+ goto create_object;
+ }
+ }
+#endif
+
+ file_length = read_varlong30(f, 3);
+ if (!(xflags & XMIT_SAME_TIME)) {
+ if (protocol_version >= 30) {
+ modtime = read_varlong(f, 4);
+#if SIZEOF_TIME_T < SIZEOF_INT64
+ if (!am_generator && (int64)(time_t)modtime != modtime) {
+ rprintf(FERROR_XFER,
+ "Time value of %s truncated on receiver.\n",
+ lastname);
+ }
+#endif
+ } else
+ modtime = read_int(f);
+ }
+ if (xflags & XMIT_MOD_NSEC)
+ modtime_nsec = read_varint(f);
+ else
+ modtime_nsec = 0;
+ if (!(xflags & XMIT_SAME_MODE))
+ mode = from_wire_mode(read_int(f));
+
+ if (chmod_modes && !S_ISLNK(mode) && mode)
+ mode = tweak_mode(mode, chmod_modes);
+
+ if (preserve_uid && !(xflags & XMIT_SAME_UID)) {
+ if (protocol_version < 30)
+ uid = (uid_t)read_int(f);
+ else {
+ uid = (uid_t)read_varint(f);
+ if (xflags & XMIT_USER_NAME_FOLLOWS)
+ uid = recv_user_name(f, uid);
+ else if (inc_recurse && am_root && (!numeric_ids || usermap))
+ uid = match_uid(uid);
+ }
+ }
+ if (preserve_gid && !(xflags & XMIT_SAME_GID)) {
+ if (protocol_version < 30)
+ gid = (gid_t)read_int(f);
+ else {
+ gid = (gid_t)read_varint(f);
+ gid_flags = 0;
+ if (xflags & XMIT_GROUP_NAME_FOLLOWS)
+ gid = recv_group_name(f, gid, &gid_flags);
+ else if (inc_recurse && (!am_root || !numeric_ids || groupmap))
+ gid = match_gid(gid, &gid_flags);
+ }
+ }
+
+ if ((preserve_devices && IS_DEVICE(mode))
+ || (preserve_specials && IS_SPECIAL(mode) && protocol_version < 31)) {
+ if (protocol_version < 28) {
+ if (!(xflags & XMIT_SAME_RDEV_pre28))
+ rdev = (dev_t)read_int(f);
+ } else {
+ uint32 rdev_minor;
+ if (!(xflags & XMIT_SAME_RDEV_MAJOR))
+ rdev_major = read_varint30(f);
+ if (protocol_version >= 30)
+ rdev_minor = read_varint(f);
+ else if (xflags & XMIT_RDEV_MINOR_8_pre30)
+ rdev_minor = read_byte(f);
+ else
+ rdev_minor = read_int(f);
+ rdev = MAKEDEV(rdev_major, rdev_minor);
+ }
+ if (IS_DEVICE(mode))
+ extra_len += DEV_EXTRA_CNT * EXTRA_LEN;
+ file_length = 0;
+ } else if (protocol_version < 28)
+ rdev = MAKEDEV(0, 0);
+
+#ifdef SUPPORT_LINKS
+ if (preserve_links && S_ISLNK(mode)) {
+ linkname_len = read_varint30(f) + 1; /* count the '\0' */
+ if (linkname_len <= 0 || linkname_len > MAXPATHLEN) {
+ rprintf(FERROR, "overflow: linkname_len=%d\n",
+ linkname_len - 1);
+ overflow_exit("recv_file_entry");
+ }
+#ifdef ICONV_OPTION
+ /* We don't know how much extra room we need to convert
+ * the as-yet-unread symlink data, so let's hope that a
+ * double-size buffer is plenty. */
+ if (sender_symlink_iconv)
+ linkname_len *= 2;
+#endif
+ if (munge_symlinks)
+ linkname_len += SYMLINK_PREFIX_LEN;
+ }
+ else
+#endif
+ linkname_len = 0;
+
+#ifdef SUPPORT_HARD_LINKS
+ create_object:
+ if (preserve_hard_links) {
+ if (protocol_version < 28 && S_ISREG(mode))
+ xflags |= XMIT_HLINKED;
+ if (xflags & XMIT_HLINKED)
+ extra_len += (inc_recurse+1) * EXTRA_LEN;
+ }
+#endif
+
+#ifdef SUPPORT_ACLS
+ /* Directories need an extra int32 for the default ACL. */
+ if (preserve_acls && S_ISDIR(mode))
+ extra_len += EXTRA_LEN;
+#endif
+
+ if (always_checksum && S_ISREG(mode))
+ extra_len += SUM_EXTRA_CNT * EXTRA_LEN;
+
+#if SIZEOF_INT64 >= 8
+ if (file_length > 0xFFFFFFFFu && S_ISREG(mode))
+ extra_len += EXTRA_LEN;
+#endif
+#ifdef HAVE_UTIMENSAT
+ if (modtime_nsec)
+ extra_len += EXTRA_LEN;
+#endif
+ if (file_length < 0) {
+ rprintf(FERROR, "Offset underflow: file-length is negative\n");
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+
+ if (inc_recurse && S_ISDIR(mode)) {
+ if (one_file_system) {
+ /* Room to save the dir's device for -x */
+ extra_len += DEV_EXTRA_CNT * EXTRA_LEN;
+ }
+ pool = dir_flist->file_pool;
+ } else
+ pool = flist->file_pool;
+
+#if EXTRA_ROUNDING > 0
+ if (extra_len & (EXTRA_ROUNDING * EXTRA_LEN))
+ extra_len = (extra_len | (EXTRA_ROUNDING * EXTRA_LEN)) + EXTRA_LEN;
+#endif
+
+ alloc_len = FILE_STRUCT_LEN + extra_len + basename_len
+ + linkname_len;
+ bp = pool_alloc(pool, alloc_len, "recv_file_entry");
+
+ memset(bp, 0, extra_len + FILE_STRUCT_LEN);
+ bp += extra_len;
+ file = (struct file_struct *)bp;
+ bp += FILE_STRUCT_LEN;
+
+ memcpy(bp, basename, basename_len);
+
+#ifdef SUPPORT_HARD_LINKS
+ if (xflags & XMIT_HLINKED
+#ifndef CAN_HARDLINK_SYMLINK
+ && !S_ISLNK(mode)
+#endif
+#ifndef CAN_HARDLINK_SPECIAL
+ && !IS_SPECIAL(mode) && !IS_DEVICE(mode)
+#endif
+ )
+ file->flags |= FLAG_HLINKED;
+#endif
+ file->modtime = (time_t)modtime;
+#ifdef HAVE_UTIMENSAT
+ if (modtime_nsec) {
+ file->flags |= FLAG_MOD_NSEC;
+ OPT_EXTRA(file, 0)->unum = modtime_nsec;
+ }
+#endif
+ file->len32 = (uint32)file_length;
+#if SIZEOF_INT64 >= 8
+ if (file_length > 0xFFFFFFFFu && S_ISREG(mode)) {
+#if SIZEOF_CAPITAL_OFF_T < 8
+ rprintf(FERROR, "Offset overflow: attempted 64-bit file-length\n");
+ exit_cleanup(RERR_UNSUPPORTED);
+#else
+ file->flags |= FLAG_LENGTH64;
+ OPT_EXTRA(file, NSEC_BUMP(file))->unum = (uint32)(file_length >> 32);
+#endif
+ }
+#endif
+ file->mode = mode;
+ if (preserve_uid)
+ F_OWNER(file) = uid;
+ if (preserve_gid) {
+ F_GROUP(file) = gid;
+ file->flags |= gid_flags;
+ }
+ if (unsort_ndx)
+ F_NDX(file) = flist->used + flist->ndx_start;
+
+ if (basename != thisname) {
+ file->dirname = lastdir;
+ F_DEPTH(file) = lastdir_depth + 1;
+ } else
+ F_DEPTH(file) = 1;
+
+ if (S_ISDIR(mode)) {
+ if (basename_len == 1+1 && *basename == '.') /* +1 for '\0' */
+ F_DEPTH(file)--;
+ if (protocol_version >= 30) {
+ if (!(xflags & XMIT_NO_CONTENT_DIR)) {
+ if (xflags & XMIT_TOP_DIR)
+ file->flags |= FLAG_TOP_DIR;
+ file->flags |= FLAG_CONTENT_DIR;
+ } else if (xflags & XMIT_TOP_DIR)
+ file->flags |= FLAG_IMPLIED_DIR;
+ } else if (xflags & XMIT_TOP_DIR) {
+ in_del_hier = recurse;
+ del_hier_name_len = F_DEPTH(file) == 0 ? 0 : l1 + l2;
+ if (relative_paths && del_hier_name_len > 2
+ && lastname[del_hier_name_len-1] == '.'
+ && lastname[del_hier_name_len-2] == '/')
+ del_hier_name_len -= 2;
+ file->flags |= FLAG_TOP_DIR | FLAG_CONTENT_DIR;
+ } else if (in_del_hier) {
+ if (!relative_paths || !del_hier_name_len
+ || (l1 >= del_hier_name_len
+ && lastname[del_hier_name_len] == '/'))
+ file->flags |= FLAG_CONTENT_DIR;
+ else
+ in_del_hier = 0;
+ }
+ }
+
+ if (preserve_devices && IS_DEVICE(mode)) {
+ uint32 *devp = F_RDEV_P(file);
+ DEV_MAJOR(devp) = major(rdev);
+ DEV_MINOR(devp) = minor(rdev);
+ }
+
+#ifdef SUPPORT_LINKS
+ if (linkname_len) {
+ bp += basename_len;
+ if (first_hlink_ndx >= flist->ndx_start) {
+ struct file_struct *first = flist->files[first_hlink_ndx - flist->ndx_start];
+ memcpy(bp, F_SYMLINK(first), linkname_len);
+ } else {
+ if (munge_symlinks) {
+ strlcpy(bp, SYMLINK_PREFIX, linkname_len);
+ bp += SYMLINK_PREFIX_LEN;
+ linkname_len -= SYMLINK_PREFIX_LEN;
+ }
+#ifdef ICONV_OPTION
+ if (sender_symlink_iconv) {
+ xbuf outbuf, inbuf;
+
+ alloc_len = linkname_len;
+ linkname_len /= 2;
+
+ /* Read the symlink data into the end of our double-sized
+ * buffer and then convert it into the right spot. */
+ INIT_XBUF(inbuf, bp + alloc_len - linkname_len,
+ linkname_len - 1, (size_t)-1);
+ read_sbuf(f, inbuf.buf, inbuf.len);
+ INIT_XBUF(outbuf, bp, 0, alloc_len);
+
+ if (iconvbufs(ic_recv, &inbuf, &outbuf, ICB_INIT) < 0) {
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR_XFER,
+ "[%s] cannot convert symlink data for: %s (%s)\n",
+ who_am_i(), full_fname(thisname), strerror(errno));
+ bp = (char*)file->basename;
+ *bp++ = '\0';
+ outbuf.len = 0;
+ }
+ bp[outbuf.len] = '\0';
+ } else
+#endif
+ read_sbuf(f, bp, linkname_len - 1);
+ if (sanitize_paths && !munge_symlinks && *bp)
+ sanitize_path(bp, bp, "", lastdir_depth, SP_DEFAULT);
+ }
+ }
+#endif
+
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && xflags & XMIT_HLINKED) {
+ if (protocol_version >= 30) {
+ if (xflags & XMIT_HLINK_FIRST) {
+ F_HL_GNUM(file) = flist->ndx_start + flist->used;
+ } else
+ F_HL_GNUM(file) = first_hlink_ndx;
+ } else {
+ static int32 cnt = 0;
+ struct ht_int64_node *np;
+ int64 ino;
+ int32 ndx;
+ if (protocol_version < 26) {
+ dev = read_int(f);
+ ino = read_int(f);
+ } else {
+ if (!(xflags & XMIT_SAME_DEV_pre30))
+ dev = read_longint(f);
+ ino = read_longint(f);
+ }
+ np = idev_find(dev, ino);
+ ndx = (int32)(long)np->data - 1;
+ if (ndx < 0) {
+ ndx = cnt++;
+ np->data = (void*)(long)cnt;
+ }
+ F_HL_GNUM(file) = ndx;
+ }
+ }
+#endif
+
+ if (always_checksum && (S_ISREG(mode) || protocol_version < 28)) {
+ if (S_ISREG(mode))
+ bp = F_SUM(file);
+ else {
+ /* Prior to 28, we get a useless set of nulls. */
+ bp = tmp_sum;
+ }
+ if (first_hlink_ndx >= flist->ndx_start) {
+ struct file_struct *first = flist->files[first_hlink_ndx - flist->ndx_start];
+ memcpy(bp, F_SUM(first), checksum_len);
+ } else
+ read_buf(f, bp, checksum_len);
+ }
+
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && !S_ISLNK(mode))
+ receive_acl(f, file);
+#endif
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs)
+ receive_xattr(f, file);
+#endif
+
+ if (S_ISREG(mode) || S_ISLNK(mode))
+ stats.total_size += file_length;
+
+ return file;
+}
+
+/* Create a file_struct for a named file by reading its stat() information
+ * and performing extensive checks against global options.
+ *
+ * Returns a pointer to the new file struct, or NULL if there was an error
+ * or this file should be excluded.
+ *
+ * Note: Any error (here or in send_file_name) that results in the omission of
+ * an existent source file from the file list should set
+ * "io_error |= IOERR_GENERAL" to avoid deletion of the file from the
+ * destination if --delete is on. */
+struct file_struct *make_file(const char *fname, struct file_list *flist,
+ STRUCT_STAT *stp, int flags, int filter_level)
+{
+ static char *lastdir;
+ static int lastdir_len = -1;
+ struct file_struct *file;
+ char thisname[MAXPATHLEN];
+ char linkname[MAXPATHLEN];
+ int alloc_len, basename_len, linkname_len;
+ int extra_len = file_extra_cnt * EXTRA_LEN;
+ const char *basename;
+ alloc_pool_t *pool;
+ STRUCT_STAT st;
+ char *bp;
+
+ if (strlcpy(thisname, fname, sizeof thisname) >= sizeof thisname) {
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR_XFER, "skipping overly long name: %s\n", fname);
+ return NULL;
+ }
+ clean_fname(thisname, 0);
+ if (sanitize_paths)
+ sanitize_path(thisname, thisname, "", 0, SP_DEFAULT);
+
+ if (stp && (S_ISDIR(stp->st_mode) || IS_MISSING_FILE(*stp))) {
+ /* This is needed to handle a "symlink/." with a --relative
+ * dir, or a request to delete a specific file. */
+ st = *stp;
+ *linkname = '\0'; /* make IBM code checker happy */
+ } else if (readlink_stat(thisname, &st, linkname) != 0) {
+ int save_errno = errno;
+ /* See if file is excluded before reporting an error. */
+ if (filter_level != NO_FILTERS
+ && (is_excluded(thisname, 0, filter_level)
+ || is_excluded(thisname, 1, filter_level))) {
+ if (ignore_perishable && save_errno != ENOENT)
+ non_perishable_cnt++;
+ return NULL;
+ }
+ if (save_errno == ENOENT) {
+#ifdef SUPPORT_LINKS
+ /* When our options tell us to follow a symlink that
+ * points nowhere, tell the user about the symlink
+ * instead of giving a "vanished" message. We only
+ * dereference a symlink if one of the --copy*links
+ * options was specified, so there's no need for the
+ * extra lstat() if one of these options isn't on. */
+ if ((copy_links || copy_unsafe_links || copy_dirlinks)
+ && x_lstat(thisname, &st, NULL) == 0
+ && S_ISLNK(st.st_mode)) {
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR_XFER, "symlink has no referent: %s\n",
+ full_fname(thisname));
+ } else
+#endif
+ {
+ enum logcode c = am_daemon && protocol_version < 28
+ ? FERROR : FWARNING;
+ io_error |= IOERR_VANISHED;
+ rprintf(c, "file has vanished: %s\n",
+ full_fname(thisname));
+ }
+ } else {
+ io_error |= IOERR_GENERAL;
+ rsyserr(FERROR_XFER, save_errno, "readlink_stat(%s) failed",
+ full_fname(thisname));
+ }
+ return NULL;
+ } else if (IS_MISSING_FILE(st)) {
+ io_error |= IOERR_GENERAL;
+ rprintf(FINFO, "skipping file with bogus (zero) st_mode: %s\n",
+ full_fname(thisname));
+ return NULL;
+ }
+
+ if (filter_level == NO_FILTERS)
+ goto skip_filters;
+
+ if (S_ISDIR(st.st_mode)) {
+ if (!xfer_dirs) {
+ rprintf(FINFO, "skipping directory %s\n", thisname);
+ return NULL;
+ }
+ /* -x only affects dirs because we need to avoid recursing
+ * into a mount-point directory, not to avoid copying a
+ * symlinked file if -L (or similar) was specified. */
+ if (one_file_system && st.st_dev != filesystem_dev
+ && BITS_SETnUNSET(flags, FLAG_CONTENT_DIR, FLAG_TOP_DIR)) {
+ if (one_file_system > 1) {
+ if (INFO_GTE(MOUNT, 1)) {
+ rprintf(FINFO,
+ "[%s] skipping mount-point dir %s\n",
+ who_am_i(), thisname);
+ }
+ return NULL;
+ }
+ flags |= FLAG_MOUNT_DIR;
+ flags &= ~FLAG_CONTENT_DIR;
+ }
+ } else
+ flags &= ~FLAG_CONTENT_DIR;
+
+ if (is_excluded(thisname, S_ISDIR(st.st_mode) != 0, filter_level)) {
+ if (ignore_perishable)
+ non_perishable_cnt++;
+ return NULL;
+ }
+
+ if (lp_ignore_nonreadable(module_id)) {
+#ifdef SUPPORT_LINKS
+ if (!S_ISLNK(st.st_mode))
+#endif
+ if (access(thisname, R_OK) != 0)
+ return NULL;
+ }
+
+ skip_filters:
+
+ /* Only divert a directory in the main transfer. */
+ if (flist) {
+ if (flist->prev && S_ISDIR(st.st_mode)
+ && flags & FLAG_DIVERT_DIRS) {
+ /* Room for parent/sibling/next-child info. */
+ extra_len += DIRNODE_EXTRA_CNT * EXTRA_LEN;
+ if (relative_paths)
+ extra_len += PTR_EXTRA_CNT * EXTRA_LEN;
+ pool = dir_flist->file_pool;
+ } else
+ pool = flist->file_pool;
+ } else {
+#ifdef SUPPORT_ACLS
+ /* Directories need an extra int32 for the default ACL. */
+ if (preserve_acls && S_ISDIR(st.st_mode))
+ extra_len += EXTRA_LEN;
+#endif
+ pool = NULL;
+ }
+
+ if (DEBUG_GTE(FLIST, 2)) {
+ rprintf(FINFO, "[%s] make_file(%s,*,%d)\n",
+ who_am_i(), thisname, filter_level);
+ }
+
+ if ((basename = strrchr(thisname, '/')) != NULL) {
+ int len = basename++ - thisname;
+ if (len != lastdir_len || memcmp(thisname, lastdir, len) != 0) {
+ lastdir = new_array(char, len + 1);
+ memcpy(lastdir, thisname, len);
+ lastdir[len] = '\0';
+ lastdir_len = len;
+ }
+ } else
+ basename = thisname;
+ basename_len = strlen(basename) + 1; /* count the '\0' */
+
+#ifdef SUPPORT_LINKS
+ linkname_len = S_ISLNK(st.st_mode) ? strlen(linkname) + 1 : 0;
+#else
+ linkname_len = 0;
+#endif
+
+#ifdef ST_MTIME_NSEC
+ if (st.ST_MTIME_NSEC && protocol_version >= 31)
+ extra_len += EXTRA_LEN;
+#endif
+#if SIZEOF_CAPITAL_OFF_T >= 8
+ if (st.st_size > 0xFFFFFFFFu && S_ISREG(st.st_mode))
+ extra_len += EXTRA_LEN;
+#endif
+
+ if (always_checksum && am_sender && S_ISREG(st.st_mode)) {
+ file_checksum(thisname, &st, tmp_sum);
+ if (sender_keeps_checksum)
+ extra_len += SUM_EXTRA_CNT * EXTRA_LEN;
+ }
+
+#if EXTRA_ROUNDING > 0
+ if (extra_len & (EXTRA_ROUNDING * EXTRA_LEN))
+ extra_len = (extra_len | (EXTRA_ROUNDING * EXTRA_LEN)) + EXTRA_LEN;
+#endif
+
+ alloc_len = FILE_STRUCT_LEN + extra_len + basename_len
+ + linkname_len;
+ if (pool)
+ bp = pool_alloc(pool, alloc_len, "make_file");
+ else {
+ if (!(bp = new_array(char, alloc_len)))
+ out_of_memory("make_file");
+ }
+
+ memset(bp, 0, extra_len + FILE_STRUCT_LEN);
+ bp += extra_len;
+ file = (struct file_struct *)bp;
+ bp += FILE_STRUCT_LEN;
+
+ memcpy(bp, basename, basename_len);
+
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && flist && flist->prev) {
+ if (protocol_version >= 28
+ ? (!S_ISDIR(st.st_mode) && st.st_nlink > 1)
+ : S_ISREG(st.st_mode)) {
+ tmp_dev = (int64)st.st_dev;
+ tmp_ino = (int64)st.st_ino;
+ } else
+ tmp_dev = -1;
+ }
+#endif
+
+#ifdef HAVE_STRUCT_STAT_ST_RDEV
+ if (IS_DEVICE(st.st_mode)) {
+ tmp_rdev = st.st_rdev;
+ st.st_size = 0;
+ } else if (IS_SPECIAL(st.st_mode))
+ st.st_size = 0;
+#endif
+
+ file->flags = flags;
+ file->modtime = st.st_mtime;
+#ifdef ST_MTIME_NSEC
+ if (st.ST_MTIME_NSEC && protocol_version >= 31) {
+ file->flags |= FLAG_MOD_NSEC;
+ OPT_EXTRA(file, 0)->unum = st.ST_MTIME_NSEC;
+ }
+#endif
+ file->len32 = (uint32)st.st_size;
+#if SIZEOF_CAPITAL_OFF_T >= 8
+ if (st.st_size > 0xFFFFFFFFu && S_ISREG(st.st_mode)) {
+ file->flags |= FLAG_LENGTH64;
+ OPT_EXTRA(file, NSEC_BUMP(file))->unum = (uint32)(st.st_size >> 32);
+ }
+#endif
+ file->mode = st.st_mode;
+ if (preserve_uid)
+ F_OWNER(file) = st.st_uid;
+ if (preserve_gid)
+ F_GROUP(file) = st.st_gid;
+ if (am_generator && st.st_uid == our_uid)
+ file->flags |= FLAG_OWNED_BY_US;
+
+ if (basename != thisname)
+ file->dirname = lastdir;
+
+#ifdef SUPPORT_LINKS
+ if (linkname_len)
+ memcpy(bp + basename_len, linkname, linkname_len);
+#endif
+
+ if (am_sender)
+ F_PATHNAME(file) = pathname;
+ else if (!pool)
+ F_DEPTH(file) = extra_len / EXTRA_LEN;
+
+ if (basename_len == 0+1) {
+ if (!pool)
+ unmake_file(file);
+ return NULL;
+ }
+
+ if (sender_keeps_checksum && S_ISREG(st.st_mode))
+ memcpy(F_SUM(file), tmp_sum, checksum_len);
+
+ if (unsort_ndx)
+ F_NDX(file) = stats.num_dirs;
+
+ return file;
+}
+
+/* Only called for temporary file_struct entries created by make_file(). */
+void unmake_file(struct file_struct *file)
+{
+ free(REQ_EXTRA(file, F_DEPTH(file)));
+}
+
+static struct file_struct *send_file_name(int f, struct file_list *flist,
+ const char *fname, STRUCT_STAT *stp,
+ int flags, int filter_level)
+{
+ struct file_struct *file;
+
+ file = make_file(fname, flist, stp, flags, filter_level);
+ if (!file)
+ return NULL;
+
+ if (chmod_modes && !S_ISLNK(file->mode) && file->mode)
+ file->mode = tweak_mode(file->mode, chmod_modes);
+
+ if (f >= 0) {
+ char fbuf[MAXPATHLEN];
+#ifdef SUPPORT_LINKS
+ const char *symlink_name;
+ int symlink_len;
+#ifdef ICONV_OPTION
+ char symlink_buf[MAXPATHLEN];
+#endif
+#endif
+#if defined SUPPORT_ACLS || defined SUPPORT_XATTRS
+ stat_x sx;
+ init_stat_x(&sx);
+#endif
+
+#ifdef SUPPORT_LINKS
+ if (preserve_links && S_ISLNK(file->mode)) {
+ symlink_name = F_SYMLINK(file);
+ symlink_len = strlen(symlink_name);
+ if (symlink_len == 0) {
+ io_error |= IOERR_GENERAL;
+ f_name(file, fbuf);
+ rprintf(FERROR_XFER,
+ "skipping symlink with 0-length value: %s\n",
+ full_fname(fbuf));
+ return NULL;
+ }
+ } else {
+ symlink_name = NULL;
+ symlink_len = 0;
+ }
+#endif
+
+#ifdef ICONV_OPTION
+ if (ic_send != (iconv_t)-1) {
+ xbuf outbuf, inbuf;
+
+ INIT_CONST_XBUF(outbuf, fbuf);
+
+ if (file->dirname) {
+ INIT_XBUF_STRLEN(inbuf, (char*)file->dirname);
+ outbuf.size -= 2; /* Reserve room for '/' & 1 more char. */
+ if (iconvbufs(ic_send, &inbuf, &outbuf, ICB_INIT) < 0)
+ goto convert_error;
+ outbuf.size += 2;
+ fbuf[outbuf.len++] = '/';
+ }
+
+ INIT_XBUF_STRLEN(inbuf, (char*)file->basename);
+ if (iconvbufs(ic_send, &inbuf, &outbuf, ICB_INIT) < 0) {
+ convert_error:
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR_XFER,
+ "[%s] cannot convert filename: %s (%s)\n",
+ who_am_i(), f_name(file, fbuf), strerror(errno));
+ return NULL;
+ }
+ fbuf[outbuf.len] = '\0';
+
+#ifdef SUPPORT_LINKS
+ if (symlink_len && sender_symlink_iconv) {
+ INIT_XBUF(inbuf, (char*)symlink_name, symlink_len, (size_t)-1);
+ INIT_CONST_XBUF(outbuf, symlink_buf);
+ if (iconvbufs(ic_send, &inbuf, &outbuf, ICB_INIT) < 0) {
+ io_error |= IOERR_GENERAL;
+ f_name(file, fbuf);
+ rprintf(FERROR_XFER,
+ "[%s] cannot convert symlink data for: %s (%s)\n",
+ who_am_i(), full_fname(fbuf), strerror(errno));
+ return NULL;
+ }
+ symlink_buf[outbuf.len] = '\0';
+
+ symlink_name = symlink_buf;
+ symlink_len = outbuf.len;
+ }
+#endif
+ } else
+#endif
+ f_name(file, fbuf);
+
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && !S_ISLNK(file->mode)) {
+ sx.st.st_mode = file->mode;
+ if (get_acl(fname, &sx) < 0) {
+ io_error |= IOERR_GENERAL;
+ return NULL;
+ }
+ }
+#endif
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs) {
+ sx.st.st_mode = file->mode;
+ if (get_xattr(fname, &sx) < 0) {
+ io_error |= IOERR_GENERAL;
+ return NULL;
+ }
+ }
+#endif
+
+ send_file_entry(f, fbuf, file,
+#ifdef SUPPORT_LINKS
+ symlink_name, symlink_len,
+#endif
+ flist->used, flist->ndx_start);
+
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && !S_ISLNK(file->mode)) {
+ send_acl(f, &sx);
+ free_acl(&sx);
+ }
+#endif
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs) {
+ F_XATTR(file) = send_xattr(f, &sx);
+ free_xattr(&sx);
+ }
+#endif
+ }
+
+ maybe_emit_filelist_progress(flist->used + flist_count_offset);
+
+ flist_expand(flist, 1);
+ flist->files[flist->used++] = file;
+
+ return file;
+}
+
+static void send_if_directory(int f, struct file_list *flist,
+ struct file_struct *file,
+ char *fbuf, unsigned int ol,
+ int flags)
+{
+ char is_dot_dir = fbuf[ol-1] == '.' && (ol == 1 || fbuf[ol-2] == '/');
+
+ if (S_ISDIR(file->mode)
+ && !(file->flags & FLAG_MOUNT_DIR) && f_name(file, fbuf)) {
+ void *save_filters;
+ unsigned int len = strlen(fbuf);
+ if (len > 1 && fbuf[len-1] == '/')
+ fbuf[--len] = '\0';
+ save_filters = push_local_filters(fbuf, len);
+ send_directory(f, flist, fbuf, len, flags);
+ pop_local_filters(save_filters);
+ fbuf[ol] = '\0';
+ if (is_dot_dir)
+ fbuf[ol-1] = '.';
+ }
+}
+
+static int file_compare(const void *file1, const void *file2)
+{
+ return f_name_cmp(*(struct file_struct **)file1,
+ *(struct file_struct **)file2);
+}
+
+/* The guts of a merge-sort algorithm. This was derived from the glibc
+ * version, but I (Wayne) changed the merge code to do less copying and
+ * to require only half the amount of temporary memory. */
+static void fsort_tmp(struct file_struct **fp, size_t num,
+ struct file_struct **tmp)
+{
+ struct file_struct **f1, **f2, **t;
+ size_t n1, n2;
+
+ n1 = num / 2;
+ n2 = num - n1;
+ f1 = fp;
+ f2 = fp + n1;
+
+ if (n1 > 1)
+ fsort_tmp(f1, n1, tmp);
+ if (n2 > 1)
+ fsort_tmp(f2, n2, tmp);
+
+ while (f_name_cmp(*f1, *f2) <= 0) {
+ if (!--n1)
+ return;
+ f1++;
+ }
+
+ t = tmp;
+ memcpy(t, f1, n1 * PTR_SIZE);
+
+ *f1++ = *f2++, n2--;
+
+ while (n1 > 0 && n2 > 0) {
+ if (f_name_cmp(*t, *f2) <= 0)
+ *f1++ = *t++, n1--;
+ else
+ *f1++ = *f2++, n2--;
+ }
+
+ if (n1 > 0)
+ memcpy(f1, t, n1 * PTR_SIZE);
+}
+
+/* This file-struct sorting routine makes sure that any identical names in
+ * the file list stay in the same order as they were in the original list.
+ * This is particularly vital in inc_recurse mode where we expect a sort
+ * on the flist to match the exact order of a sort on the dir_flist. */
+static void fsort(struct file_struct **fp, size_t num)
+{
+ if (num <= 1)
+ return;
+
+ if (use_qsort)
+ qsort(fp, num, PTR_SIZE, file_compare);
+ else {
+ struct file_struct **tmp = new_array(struct file_struct *,
+ (num+1) / 2);
+ fsort_tmp(fp, num, tmp);
+ free(tmp);
+ }
+}
+
+/* We take an entire set of sibling dirs from the sorted flist and link them
+ * into the tree, setting the appropriate parent/child/sibling pointers. */
+static void add_dirs_to_tree(int parent_ndx, struct file_list *from_flist,
+ int dir_cnt)
+{
+ int i;
+ int32 *dp = NULL;
+ int32 *parent_dp = parent_ndx < 0 ? NULL
+ : F_DIR_NODE_P(dir_flist->sorted[parent_ndx]);
+
+ flist_expand(dir_flist, dir_cnt);
+ dir_flist->sorted = dir_flist->files;
+
+ for (i = 0; dir_cnt; i++) {
+ struct file_struct *file = from_flist->sorted[i];
+
+ if (!S_ISDIR(file->mode))
+ continue;
+
+ dir_flist->files[dir_flist->used++] = file;
+ dir_cnt--;
+
+ if (file->basename[0] == '.' && file->basename[1] == '\0')
+ continue;
+
+ if (dp)
+ DIR_NEXT_SIBLING(dp) = dir_flist->used - 1;
+ else if (parent_dp)
+ DIR_FIRST_CHILD(parent_dp) = dir_flist->used - 1;
+ else
+ send_dir_ndx = dir_flist->used - 1;
+
+ dp = F_DIR_NODE_P(file);
+ DIR_PARENT(dp) = parent_ndx;
+ DIR_FIRST_CHILD(dp) = -1;
+ }
+ if (dp)
+ DIR_NEXT_SIBLING(dp) = -1;
+}
+
+static void interpret_stat_error(const char *fname, int is_dir)
+{
+ if (errno == ENOENT) {
+ io_error |= IOERR_VANISHED;
+ rprintf(FWARNING, "%s has vanished: %s\n",
+ is_dir ? "directory" : "file", full_fname(fname));
+ } else {
+ io_error |= IOERR_GENERAL;
+ rsyserr(FERROR_XFER, errno, "link_stat %s failed",
+ full_fname(fname));
+ }
+}
+
+/* This function is normally called by the sender, but the receiving side also
+ * calls it from get_dirlist() with f set to -1 so that we just construct the
+ * file list in memory without sending it over the wire. Also, get_dirlist()
+ * might call this with f set to -2, which also indicates that local filter
+ * rules should be ignored. */
+static void send_directory(int f, struct file_list *flist, char *fbuf, int len,
+ int flags)
+{
+ struct dirent *di;
+ unsigned remainder;
+ char *p;
+ DIR *d;
+ int divert_dirs = (flags & FLAG_DIVERT_DIRS) != 0;
+ int start = flist->used;
+ int filter_level = f == -2 ? SERVER_FILTERS : ALL_FILTERS;
+
+ assert(flist != NULL);
+
+ if (!(d = opendir(fbuf))) {
+ if (errno == ENOENT) {
+ if (am_sender) /* Can abuse this for vanished error w/ENOENT: */
+ interpret_stat_error(fbuf, True);
+ return;
+ }
+ io_error |= IOERR_GENERAL;
+ rsyserr(FERROR_XFER, errno, "opendir %s failed", full_fname(fbuf));
+ return;
+ }
+
+ p = fbuf + len;
+ if (len == 1 && *fbuf == '/')
+ remainder = MAXPATHLEN - 1;
+ else if (len < MAXPATHLEN-1) {
+ *p++ = '/';
+ *p = '\0';
+ remainder = MAXPATHLEN - (len + 1);
+ } else
+ remainder = 0;
+
+ for (errno = 0, di = readdir(d); di; errno = 0, di = readdir(d)) {
+ unsigned name_len;
+ char *dname = d_name(di);
+ if (dname[0] == '.' && (dname[1] == '\0'
+ || (dname[1] == '.' && dname[2] == '\0')))
+ continue;
+ name_len = strlcpy(p, dname, remainder);
+ if (name_len >= remainder) {
+ char save = fbuf[len];
+ fbuf[len] = '\0';
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR_XFER,
+ "filename overflows max-path len by %u: %s/%s\n",
+ name_len - remainder + 1, fbuf, dname);
+ fbuf[len] = save;
+ continue;
+ }
+ if (dname[0] == '\0') {
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR_XFER,
+ "cannot send file with empty name in %s\n",
+ full_fname(fbuf));
+ continue;
+ }
+
+ send_file_name(f, flist, fbuf, NULL, flags, filter_level);
+ }
+
+ fbuf[len] = '\0';
+
+ if (errno) {
+ io_error |= IOERR_GENERAL;
+ rsyserr(FERROR_XFER, errno, "readdir(%s)", full_fname(fbuf));
+ }
+
+ closedir(d);
+
+ if (f >= 0 && recurse && !divert_dirs) {
+ int i, end = flist->used - 1;
+ /* send_if_directory() bumps flist->used, so use "end". */
+ for (i = start; i <= end; i++)
+ send_if_directory(f, flist, flist->files[i], fbuf, len, flags);
+ }
+}
+
+static void send_implied_dirs(int f, struct file_list *flist, char *fname,
+ char *start, char *limit, int flags, char name_type)
+{
+ static char lastpath[MAXPATHLEN] = "";
+ static int lastpath_len = 0;
+ static struct file_struct *lastpath_struct = NULL;
+ struct file_struct *file;
+ item_list *relname_list;
+ relnamecache **rnpp;
+ int len, need_new_dir, depth = 0;
+ filter_rule_list save_filter_list = filter_list;
+
+ flags = (flags | FLAG_IMPLIED_DIR) & ~(FLAG_TOP_DIR | FLAG_CONTENT_DIR);
+ filter_list.head = filter_list.tail = NULL; /* Don't filter implied dirs. */
+
+ if (inc_recurse) {
+ if (lastpath_struct && F_PATHNAME(lastpath_struct) == pathname
+ && lastpath_len == limit - fname
+ && strncmp(lastpath, fname, lastpath_len) == 0)
+ need_new_dir = 0;
+ else
+ need_new_dir = 1;
+ } else {
+ char *tp = fname, *lp = lastpath;
+ /* Skip any initial directories in our path that we
+ * have in common with lastpath. */
+ assert(start == fname);
+ for ( ; ; tp++, lp++) {
+ if (tp == limit) {
+ if (*lp == '/' || *lp == '\0')
+ goto done;
+ break;
+ }
+ if (*lp != *tp)
+ break;
+ if (*tp == '/') {
+ start = tp;
+ depth++;
+ }
+ }
+ need_new_dir = 1;
+ }
+
+ if (need_new_dir) {
+ int save_copy_links = copy_links;
+ int save_xfer_dirs = xfer_dirs;
+ char *slash;
+
+ copy_links = xfer_dirs = 1;
+
+ *limit = '\0';
+
+ for (slash = start; (slash = strchr(slash+1, '/')) != NULL; ) {
+ *slash = '\0';
+ file = send_file_name(f, flist, fname, NULL, flags, ALL_FILTERS);
+ depth++;
+ if (!inc_recurse && file && S_ISDIR(file->mode))
+ change_local_filter_dir(fname, strlen(fname), depth);
+ *slash = '/';
+ }
+
+ file = send_file_name(f, flist, fname, NULL, flags, ALL_FILTERS);
+ if (inc_recurse) {
+ if (file && !S_ISDIR(file->mode))
+ file = NULL;
+ lastpath_struct = file;
+ } else if (file && S_ISDIR(file->mode))
+ change_local_filter_dir(fname, strlen(fname), ++depth);
+
+ strlcpy(lastpath, fname, sizeof lastpath);
+ lastpath_len = limit - fname;
+
+ *limit = '/';
+
+ copy_links = save_copy_links;
+ xfer_dirs = save_xfer_dirs;
+
+ if (!inc_recurse)
+ goto done;
+ }
+
+ if (!lastpath_struct)
+ goto done; /* dir must have vanished */
+
+ len = strlen(limit+1);
+ memcpy(&relname_list, F_DIR_RELNAMES_P(lastpath_struct), sizeof relname_list);
+ if (!relname_list) {
+ if (!(relname_list = new0(item_list)))
+ out_of_memory("send_implied_dirs");
+ memcpy(F_DIR_RELNAMES_P(lastpath_struct), &relname_list, sizeof relname_list);
+ }
+ rnpp = EXPAND_ITEM_LIST(relname_list, relnamecache *, 32);
+ if (!(*rnpp = (relnamecache*)new_array(char, sizeof (relnamecache) + len)))
+ out_of_memory("send_implied_dirs");
+ (*rnpp)->name_type = name_type;
+ strlcpy((*rnpp)->fname, limit+1, len + 1);
+
+done:
+ filter_list = save_filter_list;
+}
+
+static NORETURN void fatal_unsafe_io_error(void)
+{
+ /* This (sadly) can only happen when pushing data because
+ * the sender does not know about what kind of delete
+ * is in effect on the receiving side when pulling. */
+ rprintf(FERROR_XFER, "FATAL I/O ERROR: dying to avoid a --delete-%s issue with a pre-3.0.7 receiver.\n",
+ delete_during == 2 ? "delay" : "during");
+ exit_cleanup(RERR_UNSUPPORTED);
+}
+
+static void send1extra(int f, struct file_struct *file, struct file_list *flist)
+{
+ char fbuf[MAXPATHLEN];
+ item_list *relname_list;
+ int len, dlen, flags = FLAG_DIVERT_DIRS | FLAG_CONTENT_DIR;
+ size_t j;
+
+ f_name(file, fbuf);
+ dlen = strlen(fbuf);
+
+ if (!change_pathname(file, NULL, 0))
+ exit_cleanup(RERR_FILESELECT);
+
+ change_local_filter_dir(fbuf, dlen, send_dir_depth);
+
+ if (file->flags & FLAG_CONTENT_DIR) {
+ if (one_file_system) {
+ STRUCT_STAT st;
+ if (link_stat(fbuf, &st, copy_dirlinks) != 0) {
+ interpret_stat_error(fbuf, True);
+ return;
+ }
+ filesystem_dev = st.st_dev;
+ }
+ send_directory(f, flist, fbuf, dlen, flags);
+ }
+
+ if (!relative_paths)
+ return;
+
+ memcpy(&relname_list, F_DIR_RELNAMES_P(file), sizeof relname_list);
+ if (!relname_list)
+ return;
+
+ for (j = 0; j < relname_list->count; j++) {
+ char *slash;
+ relnamecache *rnp = ((relnamecache**)relname_list->items)[j];
+ char name_type = rnp->name_type;
+
+ fbuf[dlen] = '/';
+ len = strlcpy(fbuf + dlen + 1, rnp->fname, sizeof fbuf - dlen - 1);
+ free(rnp);
+ if (len >= (int)sizeof fbuf)
+ continue; /* Impossible... */
+
+ slash = strchr(fbuf+dlen+1, '/');
+ if (slash) {
+ send_implied_dirs(f, flist, fbuf, fbuf+dlen+1, slash, flags, name_type);
+ continue;
+ }
+
+ if (name_type != NORMAL_NAME) {
+ STRUCT_STAT st;
+ if (name_type == MISSING_NAME)
+ memset(&st, 0, sizeof st);
+ else if (link_stat(fbuf, &st, 1) != 0) {
+ interpret_stat_error(fbuf, True);
+ continue;
+ }
+ send_file_name(f, flist, fbuf, &st, FLAG_TOP_DIR | flags, ALL_FILTERS);
+ } else
+ send_file_name(f, flist, fbuf, NULL, FLAG_TOP_DIR | flags, ALL_FILTERS);
+ }
+
+ free(relname_list);
+}
+
+void send_extra_file_list(int f, int at_least)
+{
+ struct file_list *flist;
+ int64 start_write;
+ uint16 prev_flags;
+ int save_io_error = io_error;
+
+ if (flist_eof)
+ return;
+
+ if (at_least < 0)
+ at_least = file_total - file_old_total + 1;
+
+ /* Keep sending data until we have the requested number of
+ * files in the upcoming file-lists. */
+ while (file_total - file_old_total < at_least) {
+ struct file_struct *file = dir_flist->sorted[send_dir_ndx];
+ int dir_ndx, dstart = stats.num_dirs;
+ const char *pathname = F_PATHNAME(file);
+ int32 *dp;
+
+ flist = flist_new(0, "send_extra_file_list");
+ start_write = stats.total_written;
+
+ if (unsort_ndx)
+ dir_ndx = F_NDX(file);
+ else
+ dir_ndx = send_dir_ndx;
+ write_ndx(f, NDX_FLIST_OFFSET - dir_ndx);
+ flist->parent_ndx = dir_ndx;
+
+ send1extra(f, file, flist);
+ prev_flags = file->flags;
+ dp = F_DIR_NODE_P(file);
+
+ /* If there are any duplicate directory names that follow, we
+ * send all the dirs together in one file-list. The dir_flist
+ * tree links all the child subdirs onto the last dup dir. */
+ while ((dir_ndx = DIR_NEXT_SIBLING(dp)) >= 0
+ && dir_flist->sorted[dir_ndx]->flags & FLAG_DUPLICATE) {
+ send_dir_ndx = dir_ndx;
+ file = dir_flist->sorted[dir_ndx];
+ /* Try to avoid some duplicate scanning of identical dirs. */
+ if (F_PATHNAME(file) == pathname && prev_flags & FLAG_CONTENT_DIR)
+ file->flags &= ~FLAG_CONTENT_DIR;
+ send1extra(f, file, flist);
+ prev_flags = file->flags;
+ dp = F_DIR_NODE_P(file);
+ }
+
+ if (io_error == save_io_error || ignore_errors)
+ write_byte(f, 0);
+ else if (use_safe_inc_flist) {
+ write_shortint(f, XMIT_EXTENDED_FLAGS|XMIT_IO_ERROR_ENDLIST);
+ write_varint(f, io_error);
+ } else {
+ if (delete_during)
+ fatal_unsafe_io_error();
+ write_byte(f, 0);
+ }
+
+ if (need_unsorted_flist) {
+ if (!(flist->sorted = new_array(struct file_struct *, flist->used)))
+ out_of_memory("send_extra_file_list");
+ memcpy(flist->sorted, flist->files,
+ flist->used * sizeof (struct file_struct*));
+ } else
+ flist->sorted = flist->files;
+
+ flist_sort_and_clean(flist, 0);
+
+ add_dirs_to_tree(send_dir_ndx, flist, stats.num_dirs - dstart);
+ flist_done_allocating(flist);
+
+ file_total += flist->used;
+ stats.flist_size += stats.total_written - start_write;
+ stats.num_files += flist->used;
+ if (DEBUG_GTE(FLIST, 3))
+ output_flist(flist);
+
+ if (DIR_FIRST_CHILD(dp) >= 0) {
+ send_dir_ndx = DIR_FIRST_CHILD(dp);
+ send_dir_depth++;
+ } else {
+ while (DIR_NEXT_SIBLING(dp) < 0) {
+ if ((send_dir_ndx = DIR_PARENT(dp)) < 0) {
+ write_ndx(f, NDX_FLIST_EOF);
+ flist_eof = 1;
+ if (DEBUG_GTE(FLIST, 3))
+ rprintf(FINFO, "[%s] flist_eof=1\n", who_am_i());
+ change_local_filter_dir(NULL, 0, 0);
+ goto finish;
+ }
+ send_dir_depth--;
+ file = dir_flist->sorted[send_dir_ndx];
+ dp = F_DIR_NODE_P(file);
+ }
+ send_dir_ndx = DIR_NEXT_SIBLING(dp);
+ }
+ }
+
+ finish:
+ if (io_error != save_io_error && protocol_version == 30 && !ignore_errors)
+ send_msg_int(MSG_IO_ERROR, io_error);
+}
+
+struct file_list *send_file_list(int f, int argc, char *argv[])
+{
+ static const char *lastdir;
+ static int lastdir_len = -1;
+ int len, dirlen;
+ STRUCT_STAT st;
+ char *p, *dir;
+ struct file_list *flist;
+ struct timeval start_tv, end_tv;
+ int64 start_write;
+ int use_ff_fd = 0;
+ int disable_buffering, reenable_multiplex = -1;
+ int flags = recurse ? FLAG_CONTENT_DIR : 0;
+ int reading_remotely = filesfrom_host != NULL;
+ int rl_flags = (reading_remotely ? 0 : RL_DUMP_COMMENTS)
+#ifdef ICONV_OPTION
+ | (filesfrom_convert ? RL_CONVERT : 0)
+#endif
+ | (eol_nulls || reading_remotely ? RL_EOL_NULLS : 0);
+ int implied_dot_dir = 0;
+
+ rprintf(FLOG, "building file list\n");
+ if (show_filelist_p())
+ start_filelist_progress("building file list");
+ else if (inc_recurse && INFO_GTE(FLIST, 1) && !am_server)
+ rprintf(FCLIENT, "sending incremental file list\n");
+
+ start_write = stats.total_written;
+ gettimeofday(&start_tv, NULL);
+
+ if (relative_paths && protocol_version >= 30)
+ implied_dirs = 1; /* We send flagged implied dirs */
+
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && protocol_version >= 30 && !cur_flist)
+ init_hard_links();
+#endif
+
+ flist = cur_flist = flist_new(0, "send_file_list");
+ if (inc_recurse) {
+ dir_flist = flist_new(FLIST_TEMP, "send_file_list");
+ flags |= FLAG_DIVERT_DIRS;
+ } else
+ dir_flist = cur_flist;
+
+ disable_buffering = io_start_buffering_out(f);
+ if (filesfrom_fd >= 0) {
+ if (argv[0] && !change_dir(argv[0], CD_NORMAL)) {
+ rsyserr(FERROR_XFER, errno, "change_dir %s failed",
+ full_fname(argv[0]));
+ exit_cleanup(RERR_FILESELECT);
+ }
+ if (protocol_version < 31) {
+ /* Older protocols send the files-from data w/o packaging
+ * it in multiplexed I/O packets, so temporarily switch
+ * to buffered I/O to match this behavior. */
+ reenable_multiplex = io_end_multiplex_in(MPLX_TO_BUFFERED);
+ }
+ use_ff_fd = 1;
+ }
+
+ if (!orig_dir)
+ orig_dir = strdup(curr_dir);
+
+ while (1) {
+ char fbuf[MAXPATHLEN], *fn, name_type;
+
+ if (use_ff_fd) {
+ if (read_line(filesfrom_fd, fbuf, sizeof fbuf, rl_flags) == 0)
+ break;
+ sanitize_path(fbuf, fbuf, "", 0, SP_KEEP_DOT_DIRS);
+ } else {
+ if (argc-- == 0)
+ break;
+ strlcpy(fbuf, *argv++, MAXPATHLEN);
+ if (sanitize_paths)
+ sanitize_path(fbuf, fbuf, "", 0, SP_KEEP_DOT_DIRS);
+ }
+
+ len = strlen(fbuf);
+ if (relative_paths) {
+ /* We clean up fbuf below. */
+ name_type = NORMAL_NAME;
+ } else if (!len || fbuf[len - 1] == '/') {
+ if (len == 2 && fbuf[0] == '.') {
+ /* Turn "./" into just "." rather than "./." */
+ fbuf[--len] = '\0';
+ } else {
+ if (len + 1 >= MAXPATHLEN)
+ overflow_exit("send_file_list");
+ fbuf[len++] = '.';
+ fbuf[len] = '\0';
+ }
+ name_type = DOTDIR_NAME;
+ } else if (len > 1 && fbuf[len-1] == '.' && fbuf[len-2] == '.'
+ && (len == 2 || fbuf[len-3] == '/')) {
+ if (len + 2 >= MAXPATHLEN)
+ overflow_exit("send_file_list");
+ fbuf[len++] = '/';
+ fbuf[len++] = '.';
+ fbuf[len] = '\0';
+ name_type = DOTDIR_NAME;
+ } else if (fbuf[len-1] == '.' && (len == 1 || fbuf[len-2] == '/'))
+ name_type = DOTDIR_NAME;
+ else
+ name_type = NORMAL_NAME;
+
+ dir = NULL;
+
+ if (!relative_paths) {
+ p = strrchr(fbuf, '/');
+ if (p) {
+ *p = '\0';
+ if (p == fbuf)
+ dir = "/";
+ else
+ dir = fbuf;
+ len -= p - fbuf + 1;
+ fn = p + 1;
+ } else
+ fn = fbuf;
+ } else {
+ if ((p = strstr(fbuf, "/./")) != NULL) {
+ *p = '\0';
+ if (p == fbuf)
+ dir = "/";
+ else {
+ dir = fbuf;
+ clean_fname(dir, 0);
+ }
+ fn = p + 3;
+ while (*fn == '/')
+ fn++;
+ if (!*fn)
+ *--fn = '\0'; /* ensure room for '.' */
+ } else
+ fn = fbuf;
+ /* A leading ./ can be used in relative mode to affect
+ * the dest dir without its name being in the path. */
+ if (*fn == '.' && fn[1] == '/' && fn[2] && !implied_dot_dir)
+ implied_dot_dir = -1;
+ len = clean_fname(fn, CFN_KEEP_TRAILING_SLASH
+ | CFN_DROP_TRAILING_DOT_DIR);
+ if (len == 1) {
+ if (fn[0] == '/') {
+ fn = "/.";
+ len = 2;
+ name_type = DOTDIR_NAME;
+ } else if (fn[0] == '.')
+ name_type = DOTDIR_NAME;
+ } else if (fn[len-1] == '/') {
+ fn[--len] = '\0';
+ if (len == 1 && *fn == '.')
+ name_type = DOTDIR_NAME;
+ else
+ name_type = SLASH_ENDING_NAME;
+ }
+ /* Reject a ".." dir in the active part of the path. */
+ for (p = fn; (p = strstr(p, "..")) != NULL; p += 2) {
+ if ((p[2] == '/' || p[2] == '\0')
+ && (p == fn || p[-1] == '/')) {
+ rprintf(FERROR,
+ "found \"..\" dir in relative path: %s\n",
+ fn);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ }
+ }
+
+ if (!*fn) {
+ len = 1;
+ fn = ".";
+ name_type = DOTDIR_NAME;
+ }
+
+ dirlen = dir ? strlen(dir) : 0;
+ if (dirlen != lastdir_len || memcmp(lastdir, dir, dirlen) != 0) {
+ if (!change_pathname(NULL, dir, -dirlen))
+ goto bad_path;
+ lastdir = pathname;
+ lastdir_len = pathname_len;
+ } else if (!change_pathname(NULL, lastdir, lastdir_len)) {
+ bad_path:
+ if (implied_dot_dir < 0)
+ implied_dot_dir = 0;
+ continue;
+ }
+
+ if (implied_dot_dir < 0) {
+ implied_dot_dir = 1;
+ send_file_name(f, flist, ".", NULL, (flags | FLAG_IMPLIED_DIR) & ~FLAG_CONTENT_DIR, ALL_FILTERS);
+ }
+
+ if (fn != fbuf)
+ memmove(fbuf, fn, len + 1);
+
+ if (link_stat(fbuf, &st, copy_dirlinks || name_type != NORMAL_NAME) != 0
+ || (name_type != DOTDIR_NAME && is_daemon_excluded(fbuf, S_ISDIR(st.st_mode)))
+ || (relative_paths && path_is_daemon_excluded(fbuf, 1))) {
+ if (errno != ENOENT || missing_args == 0) {
+ /* This is a transfer error, but inhibit deletion
+ * only if we might be omitting an existing file. */
+ if (errno != ENOENT)
+ io_error |= IOERR_GENERAL;
+ rsyserr(FERROR_XFER, errno, "link_stat %s failed",
+ full_fname(fbuf));
+ continue;
+ } else if (missing_args == 1) {
+ /* Just ignore the arg. */
+ continue;
+ } else /* (missing_args == 2) */ {
+ /* Send the arg as a "missing" entry with
+ * mode 0, which tells the generator to delete it. */
+ memset(&st, 0, sizeof st);
+ }
+ }
+
+ /* A dot-dir should not be excluded! */
+ if (name_type != DOTDIR_NAME && st.st_mode != 0
+ && is_excluded(fbuf, S_ISDIR(st.st_mode) != 0, ALL_FILTERS))
+ continue;
+
+ if (S_ISDIR(st.st_mode) && !xfer_dirs) {
+ rprintf(FINFO, "skipping directory %s\n", fbuf);
+ continue;
+ }
+
+ if (inc_recurse && relative_paths && *fbuf) {
+ if ((p = strchr(fbuf+1, '/')) != NULL) {
+ if (p - fbuf == 1 && *fbuf == '.') {
+ if ((fn = strchr(p+1, '/')) != NULL)
+ p = fn;
+ } else
+ fn = p;
+ send_implied_dirs(f, flist, fbuf, fbuf, p, flags,
+ IS_MISSING_FILE(st) ? MISSING_NAME : name_type);
+ if (fn == p)
+ continue;
+ }
+ } else if (implied_dirs && (p=strrchr(fbuf,'/')) && p != fbuf) {
+ /* Send the implied directories at the start of the
+ * source spec, so we get their permissions right. */
+ send_implied_dirs(f, flist, fbuf, fbuf, p, flags, 0);
+ }
+
+ if (one_file_system)
+ filesystem_dev = st.st_dev;
+
+ if (recurse || (xfer_dirs && name_type != NORMAL_NAME)) {
+ struct file_struct *file;
+ file = send_file_name(f, flist, fbuf, &st,
+ FLAG_TOP_DIR | FLAG_CONTENT_DIR | flags,
+ NO_FILTERS);
+ if (!file)
+ continue;
+ if (inc_recurse) {
+ if (name_type == DOTDIR_NAME) {
+ if (send_dir_depth < 0) {
+ send_dir_depth = 0;
+ change_local_filter_dir(fbuf, len, send_dir_depth);
+ }
+ send_directory(f, flist, fbuf, len, flags);
+ }
+ } else
+ send_if_directory(f, flist, file, fbuf, len, flags);
+ } else
+ send_file_name(f, flist, fbuf, &st, flags, NO_FILTERS);
+ }
+
+ if (reenable_multiplex >= 0)
+ io_start_multiplex_in(reenable_multiplex);
+
+ gettimeofday(&end_tv, NULL);
+ stats.flist_buildtime = (int64)(end_tv.tv_sec - start_tv.tv_sec) * 1000
+ + (end_tv.tv_usec - start_tv.tv_usec) / 1000;
+ if (stats.flist_buildtime == 0)
+ stats.flist_buildtime = 1;
+ start_tv = end_tv;
+
+ /* Indicate end of file list */
+ if (io_error == 0 || ignore_errors)
+ write_byte(f, 0);
+ else if (use_safe_inc_flist) {
+ write_shortint(f, XMIT_EXTENDED_FLAGS|XMIT_IO_ERROR_ENDLIST);
+ write_varint(f, io_error);
+ } else {
+ if (delete_during && inc_recurse)
+ fatal_unsafe_io_error();
+ write_byte(f, 0);
+ }
+
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && protocol_version >= 30 && !inc_recurse)
+ idev_destroy();
+#endif
+
+ if (show_filelist_p())
+ finish_filelist_progress(flist);
+
+ gettimeofday(&end_tv, NULL);
+ stats.flist_xfertime = (int64)(end_tv.tv_sec - start_tv.tv_sec) * 1000
+ + (end_tv.tv_usec - start_tv.tv_usec) / 1000;
+
+ /* When converting names, both sides keep an unsorted file-list array
+ * because the names will differ on the sending and receiving sides
+ * (both sides will use the unsorted index number for each item). */
+
+ /* Sort the list without removing any duplicates. This allows the
+ * receiving side to ask for whatever name it kept. For incremental
+ * recursion mode, the sender marks duplicate dirs so that it can
+ * send them together in a single file-list. */
+ if (need_unsorted_flist) {
+ if (!(flist->sorted = new_array(struct file_struct *, flist->used)))
+ out_of_memory("send_file_list");
+ memcpy(flist->sorted, flist->files,
+ flist->used * sizeof (struct file_struct*));
+ } else
+ flist->sorted = flist->files;
+ flist_sort_and_clean(flist, 0);
+ file_total += flist->used;
+ file_old_total += flist->used;
+
+ if (numeric_ids <= 0 && !inc_recurse)
+ send_id_list(f);
+
+ /* send the io_error flag */
+ if (protocol_version < 30)
+ write_int(f, ignore_errors ? 0 : io_error);
+ else if (!use_safe_inc_flist && io_error && !ignore_errors)
+ send_msg_int(MSG_IO_ERROR, io_error);
+
+ if (disable_buffering)
+ io_end_buffering_out(IOBUF_FREE_BUFS);
+
+ stats.flist_size = stats.total_written - start_write;
+ stats.num_files = flist->used;
+
+ if (DEBUG_GTE(FLIST, 3))
+ output_flist(flist);
+
+ if (DEBUG_GTE(FLIST, 2))
+ rprintf(FINFO, "send_file_list done\n");
+
+ if (inc_recurse) {
+ send_dir_depth = 1;
+ add_dirs_to_tree(-1, flist, stats.num_dirs);
+ if (!file_total || strcmp(flist->sorted[flist->low]->basename, ".") != 0)
+ flist->parent_ndx = -1;
+ flist_done_allocating(flist);
+ if (send_dir_ndx < 0) {
+ write_ndx(f, NDX_FLIST_EOF);
+ flist_eof = 1;
+ if (DEBUG_GTE(FLIST, 3))
+ rprintf(FINFO, "[%s] flist_eof=1\n", who_am_i());
+ }
+ else if (file_total == 1) {
+ /* If we're creating incremental file-lists and there
+ * was just 1 item in the first file-list, send 1 more
+ * file-list to check if this is a 1-file xfer. */
+ send_extra_file_list(f, 1);
+ }
+ } else {
+ flist_eof = 1;
+ if (DEBUG_GTE(FLIST, 3))
+ rprintf(FINFO, "[%s] flist_eof=1\n", who_am_i());
+ }
+
+ return flist;
+}
+
+struct file_list *recv_file_list(int f)
+{
+ struct file_list *flist;
+ int dstart, flags;
+ int64 start_read;
+
+ if (!first_flist) {
+ if (show_filelist_p())
+ start_filelist_progress("receiving file list");
+ else if (inc_recurse && INFO_GTE(FLIST, 1) && !am_server)
+ rprintf(FCLIENT, "receiving incremental file list\n");
+ rprintf(FLOG, "receiving file list\n");
+ if (usermap)
+ parse_name_map(usermap, True);
+ if (groupmap)
+ parse_name_map(groupmap, False);
+ }
+
+ start_read = stats.total_read;
+
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && !first_flist)
+ init_hard_links();
+#endif
+
+ flist = flist_new(0, "recv_file_list");
+
+ if (inc_recurse) {
+ if (flist->ndx_start == 1)
+ dir_flist = flist_new(FLIST_TEMP, "recv_file_list");
+ dstart = dir_flist->used;
+ } else {
+ dir_flist = flist;
+ dstart = 0;
+ }
+
+ while ((flags = read_byte(f)) != 0) {
+ struct file_struct *file;
+
+ if (protocol_version >= 28 && (flags & XMIT_EXTENDED_FLAGS))
+ flags |= read_byte(f) << 8;
+
+ if (flags == (XMIT_EXTENDED_FLAGS|XMIT_IO_ERROR_ENDLIST)) {
+ int err;
+ if (!use_safe_inc_flist) {
+ rprintf(FERROR, "Invalid flist flag: %x\n", flags);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ err = read_varint(f);
+ if (!ignore_errors)
+ io_error |= err;
+ break;
+ }
+
+ flist_expand(flist, 1);
+ file = recv_file_entry(f, flist, flags);
+
+ if (S_ISREG(file->mode)) {
+ /* Already counted */
+ } else if (S_ISDIR(file->mode)) {
+ if (inc_recurse) {
+ flist_expand(dir_flist, 1);
+ dir_flist->files[dir_flist->used++] = file;
+ }
+ stats.num_dirs++;
+ } else if (S_ISLNK(file->mode))
+ stats.num_symlinks++;
+ else if (IS_DEVICE(file->mode))
+ stats.num_symlinks++;
+ else
+ stats.num_specials++;
+
+ flist->files[flist->used++] = file;
+
+ maybe_emit_filelist_progress(flist->used);
+
+ if (DEBUG_GTE(FLIST, 2)) {
+ char *name = f_name(file, NULL);
+ rprintf(FINFO, "recv_file_name(%s)\n", NS(name));
+ }
+ }
+ file_total += flist->used;
+
+ if (DEBUG_GTE(FLIST, 2))
+ rprintf(FINFO, "received %d names\n", flist->used);
+
+ if (show_filelist_p())
+ finish_filelist_progress(flist);
+
+ if (need_unsorted_flist) {
+ /* Create an extra array of index pointers that we can sort for
+ * the generator's use (for wading through the files in sorted
+ * order and for calling flist_find()). We keep the "files"
+ * list unsorted for our exchange of index numbers with the
+ * other side (since their names may not sort the same). */
+ if (!(flist->sorted = new_array(struct file_struct *, flist->used)))
+ out_of_memory("recv_file_list");
+ memcpy(flist->sorted, flist->files,
+ flist->used * sizeof (struct file_struct*));
+ if (inc_recurse && dir_flist->used > dstart) {
+ static int dir_flist_malloced = 0;
+ if (dir_flist_malloced < dir_flist->malloced) {
+ dir_flist->sorted = realloc_array(dir_flist->sorted,
+ struct file_struct *,
+ dir_flist->malloced);
+ dir_flist_malloced = dir_flist->malloced;
+ }
+ memcpy(dir_flist->sorted + dstart, dir_flist->files + dstart,
+ (dir_flist->used - dstart) * sizeof (struct file_struct*));
+ fsort(dir_flist->sorted + dstart, dir_flist->used - dstart);
+ }
+ } else {
+ flist->sorted = flist->files;
+ if (inc_recurse && dir_flist->used > dstart) {
+ dir_flist->sorted = dir_flist->files;
+ fsort(dir_flist->sorted + dstart, dir_flist->used - dstart);
+ }
+ }
+
+ if (inc_recurse)
+ flist_done_allocating(flist);
+ else if (f >= 0) {
+ recv_id_list(f, flist);
+ flist_eof = 1;
+ if (DEBUG_GTE(FLIST, 3))
+ rprintf(FINFO, "[%s] flist_eof=1\n", who_am_i());
+ }
+
+ /* The --relative option sends paths with a leading slash, so we need
+ * to specify the strip_root option here. We rejected leading slashes
+ * for a non-relative transfer in recv_file_entry(). */
+ flist_sort_and_clean(flist, relative_paths);
+
+ if (protocol_version < 30) {
+ /* Recv the io_error flag */
+ int err = read_int(f);
+ if (!ignore_errors)
+ io_error |= err;
+ } else if (inc_recurse && flist->ndx_start == 1) {
+ if (!file_total || strcmp(flist->sorted[flist->low]->basename, ".") != 0)
+ flist->parent_ndx = -1;
+ }
+
+ if (DEBUG_GTE(FLIST, 3))
+ output_flist(flist);
+
+ if (DEBUG_GTE(FLIST, 2))
+ rprintf(FINFO, "recv_file_list done\n");
+
+ stats.flist_size += stats.total_read - start_read;
+ stats.num_files += flist->used;
+
+ return flist;
+}
+
+/* This is only used once by the receiver if the very first file-list
+ * has exactly one item in it. */
+void recv_additional_file_list(int f)
+{
+ struct file_list *flist;
+ int ndx = read_ndx(f);
+ if (ndx == NDX_FLIST_EOF) {
+ flist_eof = 1;
+ if (DEBUG_GTE(FLIST, 3))
+ rprintf(FINFO, "[%s] flist_eof=1\n", who_am_i());
+ change_local_filter_dir(NULL, 0, 0);
+ } else {
+ ndx = NDX_FLIST_OFFSET - ndx;
+ if (ndx < 0 || ndx >= dir_flist->used) {
+ ndx = NDX_FLIST_OFFSET - ndx;
+ rprintf(FERROR,
+ "[%s] Invalid dir index: %d (%d - %d)\n",
+ who_am_i(), ndx, NDX_FLIST_OFFSET,
+ NDX_FLIST_OFFSET - dir_flist->used + 1);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ if (DEBUG_GTE(FLIST, 3)) {
+ rprintf(FINFO, "[%s] receiving flist for dir %d\n",
+ who_am_i(), ndx);
+ }
+ flist = recv_file_list(f);
+ flist->parent_ndx = ndx;
+ }
+}
+
+/* Search for an identically-named item in the file list. Note that the
+ * items must agree in their directory-ness, or no match is returned. */
+int flist_find(struct file_list *flist, struct file_struct *f)
+{
+ int low = flist->low, high = flist->high;
+ int diff, mid, mid_up;
+
+ while (low <= high) {
+ mid = (low + high) / 2;
+ if (F_IS_ACTIVE(flist->sorted[mid]))
+ mid_up = mid;
+ else {
+ /* Scan for the next non-empty entry using the cached
+ * distance values. If the value isn't fully up-to-
+ * date, update it. */
+ mid_up = mid + F_DEPTH(flist->sorted[mid]);
+ if (!F_IS_ACTIVE(flist->sorted[mid_up])) {
+ do {
+ mid_up += F_DEPTH(flist->sorted[mid_up]);
+ } while (!F_IS_ACTIVE(flist->sorted[mid_up]));
+ F_DEPTH(flist->sorted[mid]) = mid_up - mid;
+ }
+ if (mid_up > high) {
+ /* If there's nothing left above us, set high to
+ * a non-empty entry below us and continue. */
+ high = mid - (int)flist->sorted[mid]->len32;
+ if (!F_IS_ACTIVE(flist->sorted[high])) {
+ do {
+ high -= (int)flist->sorted[high]->len32;
+ } while (!F_IS_ACTIVE(flist->sorted[high]));
+ flist->sorted[mid]->len32 = mid - high;
+ }
+ continue;
+ }
+ }
+ diff = f_name_cmp(flist->sorted[mid_up], f);
+ if (diff == 0) {
+ if (protocol_version < 29
+ && S_ISDIR(flist->sorted[mid_up]->mode)
+ != S_ISDIR(f->mode))
+ return -1;
+ return mid_up;
+ }
+ if (diff < 0)
+ low = mid_up + 1;
+ else
+ high = mid - 1;
+ }
+ return -1;
+}
+
+/* Search for an identically-named item in the file list. Differs from
+ * flist_find in that an item that agrees with "f" in directory-ness is
+ * preferred but one that does not is still found. */
+int flist_find_ignore_dirness(struct file_list *flist, struct file_struct *f)
+{
+ mode_t save_mode;
+ int ndx;
+
+ /* First look for an item that agrees in directory-ness. */
+ ndx = flist_find(flist, f);
+ if (ndx >= 0)
+ return ndx;
+
+ /* Temporarily flip f->mode to look for an item of opposite
+ * directory-ness. */
+ save_mode = f->mode;
+ f->mode = S_ISDIR(f->mode) ? S_IFREG : S_IFDIR;
+ ndx = flist_find(flist, f);
+ f->mode = save_mode;
+ return ndx;
+}
+
+/*
+ * Free up any resources a file_struct has allocated
+ * and clear the file.
+ */
+void clear_file(struct file_struct *file)
+{
+ /* The +1 zeros out the first char of the basename. */
+ memset(file, 0, FILE_STRUCT_LEN + 1);
+ /* In an empty entry, F_DEPTH() is an offset to the next non-empty
+ * entry. Likewise for len32 in the opposite direction. We assume
+ * that we're alone for now since flist_find() will adjust the counts
+ * it runs into that aren't up-to-date. */
+ file->len32 = F_DEPTH(file) = 1;
+}
+
+/* Allocate a new file list. */
+struct file_list *flist_new(int flags, char *msg)
+{
+ struct file_list *flist;
+
+ if (!(flist = new0(struct file_list)))
+ out_of_memory(msg);
+
+ if (flags & FLIST_TEMP) {
+ if (!(flist->file_pool = pool_create(SMALL_EXTENT, 0,
+ out_of_memory,
+ POOL_INTERN)))
+ out_of_memory(msg);
+ } else {
+ /* This is a doubly linked list with prev looping back to
+ * the end of the list, but the last next pointer is NULL. */
+ if (!first_flist) {
+ flist->file_pool = pool_create(NORMAL_EXTENT, 0,
+ out_of_memory,
+ POOL_INTERN);
+ if (!flist->file_pool)
+ out_of_memory(msg);
+
+ flist->ndx_start = flist->flist_num = inc_recurse ? 1 : 0;
+
+ first_flist = cur_flist = flist->prev = flist;
+ } else {
+ struct file_list *prev = first_flist->prev;
+
+ flist->file_pool = first_flist->file_pool;
+
+ flist->ndx_start = prev->ndx_start + prev->used + 1;
+ flist->flist_num = prev->flist_num + 1;
+
+ flist->prev = prev;
+ prev->next = first_flist->prev = flist;
+ }
+ flist->pool_boundary = pool_boundary(flist->file_pool, 0);
+ flist_cnt++;
+ }
+
+ return flist;
+}
+
+/* Free up all elements in a flist. */
+void flist_free(struct file_list *flist)
+{
+ if (!flist->prev) {
+ /* Was FLIST_TEMP dir-list. */
+ } else if (flist == flist->prev) {
+ first_flist = cur_flist = NULL;
+ file_total = 0;
+ flist_cnt = 0;
+ } else {
+ if (flist == cur_flist)
+ cur_flist = flist->next;
+ if (flist == first_flist)
+ first_flist = first_flist->next;
+ else {
+ flist->prev->next = flist->next;
+ if (!flist->next)
+ flist->next = first_flist;
+ }
+ flist->next->prev = flist->prev;
+ file_total -= flist->used;
+ flist_cnt--;
+ }
+
+ if (!flist->prev || !flist_cnt)
+ pool_destroy(flist->file_pool);
+ else
+ pool_free_old(flist->file_pool, flist->pool_boundary);
+
+ if (flist->sorted && flist->sorted != flist->files)
+ free(flist->sorted);
+ free(flist->files);
+ free(flist);
+}
+
+/* This routine ensures we don't have any duplicate names in our file list.
+ * duplicate names can cause corruption because of the pipelining. */
+static void flist_sort_and_clean(struct file_list *flist, int strip_root)
+{
+ char fbuf[MAXPATHLEN];
+ int i, prev_i;
+
+ if (!flist)
+ return;
+ if (flist->used == 0) {
+ flist->high = -1;
+ flist->low = 0;
+ return;
+ }
+
+ fsort(flist->sorted, flist->used);
+
+ if (!am_sender || inc_recurse) {
+ for (i = prev_i = 0; i < flist->used; i++) {
+ if (F_IS_ACTIVE(flist->sorted[i])) {
+ prev_i = i;
+ break;
+ }
+ }
+ flist->low = prev_i;
+ } else {
+ i = prev_i = flist->used - 1;
+ flist->low = 0;
+ }
+
+ while (++i < flist->used) {
+ int j;
+ struct file_struct *file = flist->sorted[i];
+
+ if (!F_IS_ACTIVE(file))
+ continue;
+ if (f_name_cmp(file, flist->sorted[prev_i]) == 0)
+ j = prev_i;
+ else if (protocol_version >= 29 && S_ISDIR(file->mode)) {
+ int save_mode = file->mode;
+ /* Make sure that this directory doesn't duplicate a
+ * non-directory earlier in the list. */
+ flist->high = prev_i;
+ file->mode = S_IFREG;
+ j = flist_find(flist, file);
+ file->mode = save_mode;
+ } else
+ j = -1;
+ if (j >= 0) {
+ int keep, drop;
+ /* If one is a dir and the other is not, we want to
+ * keep the dir because it might have contents in the
+ * list. Otherwise keep the first one. */
+ if (S_ISDIR(file->mode)) {
+ struct file_struct *fp = flist->sorted[j];
+ if (!S_ISDIR(fp->mode))
+ keep = i, drop = j;
+ else {
+ if (am_sender)
+ file->flags |= FLAG_DUPLICATE;
+ else { /* Make sure we merge our vital flags. */
+ fp->flags |= file->flags & (FLAG_TOP_DIR|FLAG_CONTENT_DIR);
+ fp->flags &= file->flags | ~FLAG_IMPLIED_DIR;
+ }
+ keep = j, drop = i;
+ }
+ } else
+ keep = j, drop = i;
+
+ if (!am_sender) {
+ if (DEBUG_GTE(DUP, 1)) {
+ rprintf(FINFO,
+ "removing duplicate name %s from file list (%d)\n",
+ f_name(file, fbuf), drop + flist->ndx_start);
+ }
+ clear_file(flist->sorted[drop]);
+ }
+
+ if (keep == i) {
+ if (flist->low == drop) {
+ for (j = drop + 1;
+ j < i && !F_IS_ACTIVE(flist->sorted[j]);
+ j++) {}
+ flist->low = j;
+ }
+ prev_i = i;
+ }
+ } else
+ prev_i = i;
+ }
+ flist->high = prev_i;
+
+ if (strip_root) {
+ /* We need to strip off the leading slashes for relative
+ * paths, but this must be done _after_ the sorting phase. */
+ for (i = flist->low; i <= flist->high; i++) {
+ struct file_struct *file = flist->sorted[i];
+
+ if (!file->dirname)
+ continue;
+ while (*file->dirname == '/')
+ file->dirname++;
+ if (!*file->dirname)
+ file->dirname = NULL;
+ }
+ }
+
+ if (prune_empty_dirs && !am_sender) {
+ int j, prev_depth = 0;
+
+ prev_i = 0; /* It's OK that this isn't really true. */
+
+ for (i = flist->low; i <= flist->high; i++) {
+ struct file_struct *fp, *file = flist->sorted[i];
+
+ /* This temporarily abuses the F_DEPTH() value for a
+ * directory that is in a chain that might get pruned.
+ * We restore the old value if it gets a reprieve. */
+ if (S_ISDIR(file->mode) && F_DEPTH(file)) {
+ /* Dump empty dirs when coming back down. */
+ for (j = prev_depth; j >= F_DEPTH(file); j--) {
+ fp = flist->sorted[prev_i];
+ if (F_DEPTH(fp) >= 0)
+ break;
+ prev_i = -F_DEPTH(fp)-1;
+ clear_file(fp);
+ }
+ prev_depth = F_DEPTH(file);
+ if (is_excluded(f_name(file, fbuf), 1,
+ ALL_FILTERS)) {
+ /* Keep dirs through this dir. */
+ for (j = prev_depth-1; ; j--) {
+ fp = flist->sorted[prev_i];
+ if (F_DEPTH(fp) >= 0)
+ break;
+ prev_i = -F_DEPTH(fp)-1;
+ F_DEPTH(fp) = j;
+ }
+ } else
+ F_DEPTH(file) = -prev_i-1;
+ prev_i = i;
+ } else {
+ /* Keep dirs through this non-dir. */
+ for (j = prev_depth; ; j--) {
+ fp = flist->sorted[prev_i];
+ if (F_DEPTH(fp) >= 0)
+ break;
+ prev_i = -F_DEPTH(fp)-1;
+ F_DEPTH(fp) = j;
+ }
+ }
+ }
+ /* Dump all remaining empty dirs. */
+ while (1) {
+ struct file_struct *fp = flist->sorted[prev_i];
+ if (F_DEPTH(fp) >= 0)
+ break;
+ prev_i = -F_DEPTH(fp)-1;
+ clear_file(fp);
+ }
+
+ for (i = flist->low; i <= flist->high; i++) {
+ if (F_IS_ACTIVE(flist->sorted[i]))
+ break;
+ }
+ flist->low = i;
+ for (i = flist->high; i >= flist->low; i--) {
+ if (F_IS_ACTIVE(flist->sorted[i]))
+ break;
+ }
+ flist->high = i;
+ }
+}
+
+static void output_flist(struct file_list *flist)
+{
+ char uidbuf[16], gidbuf[16], depthbuf[16];
+ struct file_struct *file;
+ const char *root, *dir, *slash, *name, *trail;
+ const char *who = who_am_i();
+ int i;
+
+ rprintf(FINFO, "[%s] flist start=%d, used=%d, low=%d, high=%d\n",
+ who, flist->ndx_start, flist->used, flist->low, flist->high);
+ for (i = 0; i < flist->used; i++) {
+ file = flist->files[i];
+ if ((am_root || am_sender) && uid_ndx) {
+ snprintf(uidbuf, sizeof uidbuf, " uid=%u",
+ F_OWNER(file));
+ } else
+ *uidbuf = '\0';
+ if (gid_ndx) {
+ static char parens[] = "(\0)\0\0\0";
+ char *pp = parens + (file->flags & FLAG_SKIP_GROUP ? 0 : 3);
+ snprintf(gidbuf, sizeof gidbuf, " gid=%s%u%s",
+ pp, F_GROUP(file), pp + 2);
+ } else
+ *gidbuf = '\0';
+ if (!am_sender)
+ snprintf(depthbuf, sizeof depthbuf, "%d", F_DEPTH(file));
+ if (F_IS_ACTIVE(file)) {
+ root = am_sender ? NS(F_PATHNAME(file)) : depthbuf;
+ if ((dir = file->dirname) == NULL)
+ dir = slash = "";
+ else
+ slash = "/";
+ name = file->basename;
+ trail = S_ISDIR(file->mode) ? "/" : "";
+ } else
+ root = dir = slash = name = trail = "";
+ rprintf(FINFO,
+ "[%s] i=%d %s %s%s%s%s mode=0%o len=%s%s%s flags=%x\n",
+ who, i + flist->ndx_start,
+ root, dir, slash, name, trail,
+ (int)file->mode, comma_num(F_LENGTH(file)),
+ uidbuf, gidbuf, file->flags);
+ }
+}
+
+enum fnc_state { s_DIR, s_SLASH, s_BASE, s_TRAILING };
+enum fnc_type { t_PATH, t_ITEM };
+
+static int found_prefix;
+
+/* Compare the names of two file_struct entities, similar to how strcmp()
+ * would do if it were operating on the joined strings.
+ *
+ * Some differences beginning with protocol_version 29: (1) directory names
+ * are compared with an assumed trailing slash so that they compare in a
+ * way that would cause them to sort immediately prior to any content they
+ * may have; (2) a directory of any name compares after a non-directory of
+ * any name at the same depth; (3) a directory with name "." compares prior
+ * to anything else. These changes mean that a directory and a non-dir
+ * with the same name will not compare as equal (protocol_version >= 29).
+ *
+ * The dirname component can be an empty string, but the basename component
+ * cannot (and never is in the current codebase). The basename component
+ * may be NULL (for a removed item), in which case it is considered to be
+ * after any existing item. */
+int f_name_cmp(const struct file_struct *f1, const struct file_struct *f2)
+{
+ int dif;
+ const uchar *c1, *c2;
+ enum fnc_state state1, state2;
+ enum fnc_type type1, type2;
+ enum fnc_type t_path = protocol_version >= 29 ? t_PATH : t_ITEM;
+
+ if (!f1 || !F_IS_ACTIVE(f1)) {
+ if (!f2 || !F_IS_ACTIVE(f2))
+ return 0;
+ return -1;
+ }
+ if (!f2 || !F_IS_ACTIVE(f2))
+ return 1;
+
+ c1 = (uchar*)f1->dirname;
+ c2 = (uchar*)f2->dirname;
+ if (c1 == c2)
+ c1 = c2 = NULL;
+ if (!c1) {
+ type1 = S_ISDIR(f1->mode) ? t_path : t_ITEM;
+ c1 = (const uchar*)f1->basename;
+ if (type1 == t_PATH && *c1 == '.' && !c1[1]) {
+ type1 = t_ITEM;
+ state1 = s_TRAILING;
+ c1 = (uchar*)"";
+ } else
+ state1 = s_BASE;
+ } else {
+ type1 = t_path;
+ state1 = s_DIR;
+ }
+ if (!c2) {
+ type2 = S_ISDIR(f2->mode) ? t_path : t_ITEM;
+ c2 = (const uchar*)f2->basename;
+ if (type2 == t_PATH && *c2 == '.' && !c2[1]) {
+ type2 = t_ITEM;
+ state2 = s_TRAILING;
+ c2 = (uchar*)"";
+ } else
+ state2 = s_BASE;
+ } else {
+ type2 = t_path;
+ state2 = s_DIR;
+ }
+
+ if (type1 != type2)
+ return type1 == t_PATH ? 1 : -1;
+
+ do {
+ if (!*c1) {
+ switch (state1) {
+ case s_DIR:
+ state1 = s_SLASH;
+ c1 = (uchar*)"/";
+ break;
+ case s_SLASH:
+ type1 = S_ISDIR(f1->mode) ? t_path : t_ITEM;
+ c1 = (const uchar*)f1->basename;
+ if (type1 == t_PATH && *c1 == '.' && !c1[1]) {
+ type1 = t_ITEM;
+ state1 = s_TRAILING;
+ c1 = (uchar*)"";
+ } else
+ state1 = s_BASE;
+ break;
+ case s_BASE:
+ state1 = s_TRAILING;
+ if (type1 == t_PATH) {
+ c1 = (uchar*)"/";
+ break;
+ }
+ /* FALL THROUGH */
+ case s_TRAILING:
+ type1 = t_ITEM;
+ break;
+ }
+ if (*c2 && type1 != type2)
+ return type1 == t_PATH ? 1 : -1;
+ }
+ if (!*c2) {
+ switch (state2) {
+ case s_DIR:
+ state2 = s_SLASH;
+ c2 = (uchar*)"/";
+ break;
+ case s_SLASH:
+ type2 = S_ISDIR(f2->mode) ? t_path : t_ITEM;
+ c2 = (const uchar*)f2->basename;
+ if (type2 == t_PATH && *c2 == '.' && !c2[1]) {
+ type2 = t_ITEM;
+ state2 = s_TRAILING;
+ c2 = (uchar*)"";
+ } else
+ state2 = s_BASE;
+ break;
+ case s_BASE:
+ state2 = s_TRAILING;
+ if (type2 == t_PATH) {
+ c2 = (uchar*)"/";
+ break;
+ }
+ /* FALL THROUGH */
+ case s_TRAILING:
+ found_prefix = 1;
+ if (!*c1)
+ return 0;
+ type2 = t_ITEM;
+ break;
+ }
+ if (type1 != type2)
+ return type1 == t_PATH ? 1 : -1;
+ }
+ } while ((dif = (int)*c1++ - (int)*c2++) == 0);
+
+ return dif;
+}
+
+/* Returns 1 if f1's filename has all of f2's filename as a prefix. This does
+ * not match if f2's basename is not an exact match of a path element in f1.
+ * E.g. /path/foo is not a prefix of /path/foobar/baz, but /path/foobar is. */
+int f_name_has_prefix(const struct file_struct *f1, const struct file_struct *f2)
+{
+ found_prefix = 0;
+ f_name_cmp(f1, f2);
+ return found_prefix;
+}
+
+char *f_name_buf(void)
+{
+ static char names[5][MAXPATHLEN];
+ static unsigned int n;
+
+ n = (n + 1) % (sizeof names / sizeof names[0]);
+
+ return names[n];
+}
+
+/* Return a copy of the full filename of a flist entry, using the indicated
+ * buffer or one of 5 static buffers if fbuf is NULL. No size-checking is
+ * done because we checked the size when creating the file_struct entry.
+ */
+char *f_name(const struct file_struct *f, char *fbuf)
+{
+ if (!f || !F_IS_ACTIVE(f))
+ return NULL;
+
+ if (!fbuf)
+ fbuf = f_name_buf();
+
+ if (f->dirname) {
+ int len = strlen(f->dirname);
+ memcpy(fbuf, f->dirname, len);
+ fbuf[len] = '/';
+ strlcpy(fbuf + len + 1, f->basename, MAXPATHLEN - (len + 1));
+ } else
+ strlcpy(fbuf, f->basename, MAXPATHLEN);
+
+ return fbuf;
+}
+
+/* Do a non-recursive scan of the named directory, possibly ignoring all
+ * exclude rules except for the daemon's. If "dlen" is >=0, it is the length
+ * of the dirname string, and also indicates that "dirname" is a MAXPATHLEN
+ * buffer (the functions we call will append names onto the end, but the old
+ * dir value will be restored on exit). */
+struct file_list *get_dirlist(char *dirname, int dlen, int flags)
+{
+ struct file_list *dirlist;
+ char dirbuf[MAXPATHLEN];
+ int save_recurse = recurse;
+ int save_xfer_dirs = xfer_dirs;
+ int save_prune_empty_dirs = prune_empty_dirs;
+ int senddir_fd = flags & GDL_IGNORE_FILTER_RULES ? -2 : -1;
+
+ if (dlen < 0) {
+ dlen = strlcpy(dirbuf, dirname, MAXPATHLEN);
+ if (dlen >= MAXPATHLEN)
+ return NULL;
+ dirname = dirbuf;
+ }
+
+ dirlist = flist_new(FLIST_TEMP, "get_dirlist");
+
+ recurse = 0;
+ xfer_dirs = 1;
+ send_directory(senddir_fd, dirlist, dirname, dlen, FLAG_CONTENT_DIR);
+ xfer_dirs = save_xfer_dirs;
+ recurse = save_recurse;
+ if (INFO_GTE(PROGRESS, 1))
+ flist_count_offset += dirlist->used;
+
+ prune_empty_dirs = 0;
+ dirlist->sorted = dirlist->files;
+ flist_sort_and_clean(dirlist, 0);
+ prune_empty_dirs = save_prune_empty_dirs;
+
+ if (DEBUG_GTE(FLIST, 3))
+ output_flist(dirlist);
+
+ return dirlist;
+}
diff --git a/rsync/generator.c b/rsync/generator.c
new file mode 100644
index 0000000..91009a5
--- /dev/null
+++ b/rsync/generator.c
@@ -0,0 +1,2370 @@
+/*
+ * Routines that are exclusive to the generator process.
+ *
+ * Copyright (C) 1996-2000 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2002 Martin Pool
+ * Copyright (C) 2003-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "inums.h"
+#include "ifuncs.h"
+
+extern int dry_run;
+extern int do_xfers;
+extern int stdout_format_has_i;
+extern int logfile_format_has_i;
+extern int am_root;
+extern int am_server;
+extern int am_daemon;
+extern int inc_recurse;
+extern int relative_paths;
+extern int implied_dirs;
+extern int keep_dirlinks;
+extern int preserve_acls;
+extern int preserve_xattrs;
+extern int preserve_links;
+extern int preserve_devices;
+extern int preserve_specials;
+extern int preserve_hard_links;
+extern int preserve_executability;
+extern int preserve_perms;
+extern int preserve_times;
+extern int delete_mode;
+extern int delete_before;
+extern int delete_during;
+extern int delete_after;
+extern int missing_args;
+extern int msgdone_cnt;
+extern int ignore_errors;
+extern int remove_source_files;
+extern int delay_updates;
+extern int update_only;
+extern int human_readable;
+extern int ignore_existing;
+extern int ignore_non_existing;
+extern int want_xattr_optim;
+extern int inplace;
+extern int append_mode;
+extern int make_backups;
+extern int csum_length;
+extern int ignore_times;
+extern int size_only;
+extern OFF_T max_size;
+extern OFF_T min_size;
+extern int io_error;
+extern int flist_eof;
+extern int allowed_lull;
+extern int sock_f_out;
+extern int protocol_version;
+extern int file_total;
+extern int fuzzy_basis;
+extern int always_checksum;
+extern int checksum_len;
+extern char *partial_dir;
+extern int compare_dest;
+extern int copy_dest;
+extern int link_dest;
+extern int whole_file;
+extern int list_only;
+extern int read_batch;
+extern int write_batch;
+extern int safe_symlinks;
+extern long block_size; /* "long" because popt can't set an int32. */
+extern int unsort_ndx;
+extern int max_delete;
+extern int force_delete;
+extern int one_file_system;
+extern int skipped_deletes;
+extern dev_t filesystem_dev;
+extern mode_t orig_umask;
+extern uid_t our_uid;
+extern char *tmpdir;
+extern char *basis_dir[MAX_BASIS_DIRS+1];
+extern struct file_list *cur_flist, *first_flist, *dir_flist;
+extern filter_rule_list filter_list, daemon_filter_list;
+
+int maybe_ATTRS_REPORT = 0;
+
+static dev_t dev_zero;
+static int deldelay_size = 0, deldelay_cnt = 0;
+static char *deldelay_buf = NULL;
+static int deldelay_fd = -1;
+static int loopchk_limit;
+static int dir_tweaking;
+static int symlink_timeset_failed_flags;
+static int need_retouch_dir_times;
+static int need_retouch_dir_perms;
+static const char *solo_file = NULL;
+
+enum nonregtype {
+ TYPE_DIR, TYPE_SPECIAL, TYPE_DEVICE, TYPE_SYMLINK
+};
+
+/* Forward declarations. */
+#ifdef SUPPORT_HARD_LINKS
+static void handle_skipped_hlink(struct file_struct *file, int itemizing,
+ enum logcode code, int f_out);
+#endif
+
+#define EARLY_DELAY_DONE_MSG() (!delay_updates)
+#define EARLY_DELETE_DONE_MSG() (!(delete_during == 2 || delete_after))
+
+static int start_delete_delay_temp(void)
+{
+ char fnametmp[MAXPATHLEN];
+ int save_dry_run = dry_run;
+
+ dry_run = 0;
+ if (!get_tmpname(fnametmp, "deldelay", False)
+ || (deldelay_fd = do_mkstemp(fnametmp, 0600)) < 0) {
+ rprintf(FINFO, "NOTE: Unable to create delete-delay temp file%s.\n",
+ inc_recurse ? "" : " -- switching to --delete-after");
+ delete_during = 0;
+ delete_after = !inc_recurse;
+ dry_run = save_dry_run;
+ return 0;
+ }
+ unlink(fnametmp);
+ dry_run = save_dry_run;
+ return 1;
+}
+
+static int flush_delete_delay(void)
+{
+ if (deldelay_fd < 0 && !start_delete_delay_temp())
+ return 0;
+ if (write(deldelay_fd, deldelay_buf, deldelay_cnt) != deldelay_cnt) {
+ rsyserr(FERROR, errno, "flush of delete-delay buffer");
+ delete_during = 0;
+ delete_after = !inc_recurse;
+ close(deldelay_fd);
+ return 0;
+ }
+ deldelay_cnt = 0;
+ return 1;
+}
+
+static int remember_delete(struct file_struct *file, const char *fname, int flags)
+{
+ int len;
+
+ if (deldelay_cnt == deldelay_size && !flush_delete_delay())
+ return 0;
+
+ if (flags & DEL_NO_UID_WRITE)
+ deldelay_buf[deldelay_cnt++] = '!';
+
+ while (1) {
+ len = snprintf(deldelay_buf + deldelay_cnt,
+ deldelay_size - deldelay_cnt,
+ "%x %s%c",
+ (int)file->mode, fname, '\0');
+ if ((deldelay_cnt += len) <= deldelay_size)
+ break;
+ deldelay_cnt -= len;
+ if (!flush_delete_delay())
+ return 0;
+ }
+
+ return 1;
+}
+
+static int read_delay_line(char *buf, int *flags_p)
+{
+ static int read_pos = 0;
+ int j, len, mode;
+ char *bp, *past_space;
+
+ while (1) {
+ for (j = read_pos; j < deldelay_cnt && deldelay_buf[j]; j++) {}
+ if (j < deldelay_cnt)
+ break;
+ if (deldelay_fd < 0) {
+ if (j > read_pos)
+ goto invalid_data;
+ return -1;
+ }
+ deldelay_cnt -= read_pos;
+ if (deldelay_cnt == deldelay_size)
+ goto invalid_data;
+ if (deldelay_cnt && read_pos) {
+ memmove(deldelay_buf, deldelay_buf + read_pos,
+ deldelay_cnt);
+ }
+ len = read(deldelay_fd, deldelay_buf + deldelay_cnt,
+ deldelay_size - deldelay_cnt);
+ if (len == 0) {
+ if (deldelay_cnt) {
+ rprintf(FERROR,
+ "ERROR: unexpected EOF in delete-delay file.\n");
+ }
+ return -1;
+ }
+ if (len < 0) {
+ rsyserr(FERROR, errno,
+ "reading delete-delay file");
+ return -1;
+ }
+ deldelay_cnt += len;
+ read_pos = 0;
+ }
+
+ bp = deldelay_buf + read_pos;
+ if (*bp == '!') {
+ bp++;
+ *flags_p = DEL_NO_UID_WRITE;
+ } else
+ *flags_p = 0;
+
+ if (sscanf(bp, "%x ", &mode) != 1) {
+ invalid_data:
+ rprintf(FERROR, "ERROR: invalid data in delete-delay file.\n");
+ return -1;
+ }
+ past_space = strchr(bp, ' ') + 1;
+ len = j - read_pos - (past_space - bp) + 1; /* count the '\0' */
+ read_pos = j + 1;
+
+ if (len > MAXPATHLEN) {
+ rprintf(FERROR, "ERROR: filename too long in delete-delay file.\n");
+ return -1;
+ }
+
+ /* The caller needs the name in a MAXPATHLEN buffer, so we copy it
+ * instead of returning a pointer to our buffer. */
+ memcpy(buf, past_space, len);
+
+ return mode;
+}
+
+static void do_delayed_deletions(char *delbuf)
+{
+ int mode, flags;
+
+ if (deldelay_fd >= 0) {
+ if (deldelay_cnt && !flush_delete_delay())
+ return;
+ lseek(deldelay_fd, 0, 0);
+ }
+ while ((mode = read_delay_line(delbuf, &flags)) >= 0)
+ delete_item(delbuf, mode, flags | DEL_RECURSE);
+ if (deldelay_fd >= 0)
+ close(deldelay_fd);
+}
+
+/* This function is used to implement per-directory deletion, and is used by
+ * all the --delete-WHEN options. Note that the fbuf pointer must point to a
+ * MAXPATHLEN buffer with the name of the directory in it (the functions we
+ * call will append names onto the end, but the old dir value will be restored
+ * on exit). */
+static void delete_in_dir(char *fbuf, struct file_struct *file, dev_t *fs_dev)
+{
+ static int already_warned = 0;
+ struct file_list *dirlist;
+ char delbuf[MAXPATHLEN];
+ int dlen, i;
+
+ if (!fbuf) {
+ change_local_filter_dir(NULL, 0, 0);
+ return;
+ }
+
+ if (DEBUG_GTE(DEL, 2))
+ rprintf(FINFO, "delete_in_dir(%s)\n", fbuf);
+
+ if (allowed_lull)
+ maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH);
+
+ if (io_error & IOERR_GENERAL && !ignore_errors) {
+ if (already_warned)
+ return;
+ rprintf(FINFO,
+ "IO error encountered -- skipping file deletion\n");
+ already_warned = 1;
+ return;
+ }
+
+ dlen = strlen(fbuf);
+ change_local_filter_dir(fbuf, dlen, F_DEPTH(file));
+
+ if (one_file_system) {
+ if (file->flags & FLAG_TOP_DIR)
+ filesystem_dev = *fs_dev;
+ else if (filesystem_dev != *fs_dev)
+ return;
+ }
+
+ dirlist = get_dirlist(fbuf, dlen, 0);
+
+ /* If an item in dirlist is not found in flist, delete it
+ * from the filesystem. */
+ for (i = dirlist->used; i--; ) {
+ struct file_struct *fp = dirlist->files[i];
+ if (!F_IS_ACTIVE(fp))
+ continue;
+ if (fp->flags & FLAG_MOUNT_DIR && S_ISDIR(fp->mode)) {
+ if (INFO_GTE(MOUNT, 1))
+ rprintf(FINFO, "cannot delete mount point: %s\n",
+ f_name(fp, NULL));
+ continue;
+ }
+ /* Here we want to match regardless of file type. Replacement
+ * of a file with one of another type is handled separately by
+ * a delete_item call with a DEL_MAKE_ROOM flag. */
+ if (flist_find_ignore_dirness(cur_flist, fp) < 0) {
+ int flags = DEL_RECURSE;
+ if (!(fp->mode & S_IWUSR) && !am_root && fp->flags & FLAG_OWNED_BY_US)
+ flags |= DEL_NO_UID_WRITE;
+ f_name(fp, delbuf);
+ if (delete_during == 2) {
+ if (!remember_delete(fp, delbuf, flags))
+ break;
+ } else
+ delete_item(delbuf, fp->mode, flags);
+ }
+ }
+
+ flist_free(dirlist);
+}
+
+/* This deletes any files on the receiving side that are not present on the
+ * sending side. This is used by --delete-before and --delete-after. */
+static void do_delete_pass(void)
+{
+ char fbuf[MAXPATHLEN];
+ STRUCT_STAT st;
+ int j;
+
+ /* dry_run is incremented when the destination doesn't exist yet. */
+ if (dry_run > 1 || list_only)
+ return;
+
+ for (j = 0; j < cur_flist->used; j++) {
+ struct file_struct *file = cur_flist->sorted[j];
+
+ if (!F_IS_ACTIVE(file))
+ continue;
+
+ f_name(file, fbuf);
+
+ if (!(file->flags & FLAG_CONTENT_DIR)) {
+ change_local_filter_dir(fbuf, strlen(fbuf), F_DEPTH(file));
+ continue;
+ }
+
+ if (DEBUG_GTE(DEL, 1) && file->flags & FLAG_TOP_DIR)
+ rprintf(FINFO, "deleting in %s\n", fbuf);
+
+ if (link_stat(fbuf, &st, keep_dirlinks) < 0
+ || !S_ISDIR(st.st_mode))
+ continue;
+
+ delete_in_dir(fbuf, file, &st.st_dev);
+ }
+ delete_in_dir(NULL, NULL, &dev_zero);
+
+ if (INFO_GTE(FLIST, 2) && !am_server)
+ rprintf(FINFO, " \r");
+}
+
+static inline int time_differs(struct file_struct *file, stat_x *sxp)
+{
+ return cmp_time(sxp->st.st_mtime, file->modtime);
+}
+
+static inline int perms_differ(struct file_struct *file, stat_x *sxp)
+{
+ if (preserve_perms)
+ return !BITS_EQUAL(sxp->st.st_mode, file->mode, CHMOD_BITS);
+
+ if (preserve_executability)
+ return (sxp->st.st_mode & 0111 ? 1 : 0) ^ (file->mode & 0111 ? 1 : 0);
+
+ return 0;
+}
+
+static inline int ownership_differs(struct file_struct *file, stat_x *sxp)
+{
+ if (am_root && uid_ndx && sxp->st.st_uid != (uid_t)F_OWNER(file))
+ return 1;
+
+ if (gid_ndx && !(file->flags & FLAG_SKIP_GROUP) && sxp->st.st_gid != (gid_t)F_GROUP(file))
+ return 1;
+
+ return 0;
+}
+
+#ifdef SUPPORT_ACLS
+static inline int acls_differ(const char *fname, struct file_struct *file, stat_x *sxp)
+{
+ if (preserve_acls) {
+ if (!ACL_READY(*sxp))
+ get_acl(fname, sxp);
+ if (set_acl(NULL, file, sxp, file->mode))
+ return 1;
+ }
+
+ return 0;
+}
+#endif
+
+#ifdef SUPPORT_XATTRS
+static inline int xattrs_differ(const char *fname, struct file_struct *file, stat_x *sxp)
+{
+ if (preserve_xattrs) {
+ if (!XATTR_READY(*sxp))
+ get_xattr(fname, sxp);
+ if (xattr_diff(file, sxp, 0))
+ return 1;
+ }
+
+ return 0;
+}
+#endif
+
+int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp)
+{
+ if (S_ISLNK(file->mode)) {
+#ifdef CAN_SET_SYMLINK_TIMES
+ if (preserve_times & PRESERVE_LINK_TIMES && time_differs(file, sxp))
+ return 0;
+#endif
+#ifdef CAN_CHMOD_SYMLINK
+ if (perms_differ(file, sxp))
+ return 0;
+#endif
+#ifdef CAN_CHOWN_SYMLINK
+ if (ownership_differs(file, sxp))
+ return 0;
+#endif
+#if defined SUPPORT_ACLS && 0 /* no current symlink-ACL support */
+ if (acls_differ(fname, file, sxp))
+ return 0;
+#endif
+#if defined SUPPORT_XATTRS && !defined NO_SYMLINK_XATTRS
+ if (xattrs_differ(fname, file, sxp))
+ return 0;
+#endif
+ } else {
+ if (preserve_times && time_differs(file, sxp))
+ return 0;
+ if (perms_differ(file, sxp))
+ return 0;
+ if (ownership_differs(file, sxp))
+ return 0;
+#ifdef SUPPORT_ACLS
+ if (acls_differ(fname, file, sxp))
+ return 0;
+#endif
+#ifdef SUPPORT_XATTRS
+ if (xattrs_differ(fname, file, sxp))
+ return 0;
+#endif
+ }
+
+ return 1;
+}
+
+void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statret,
+ stat_x *sxp, int32 iflags, uchar fnamecmp_type,
+ const char *xname)
+{
+ if (statret >= 0) { /* A from-dest-dir statret can == 1! */
+ int keep_time = !preserve_times ? 0
+ : S_ISDIR(file->mode) ? preserve_times & PRESERVE_DIR_TIMES
+ : S_ISLNK(file->mode) ? preserve_times & PRESERVE_LINK_TIMES
+ : 1;
+
+ if (S_ISREG(file->mode) && F_LENGTH(file) != sxp->st.st_size)
+ iflags |= ITEM_REPORT_SIZE;
+ if (file->flags & FLAG_TIME_FAILED) { /* symlinks only */
+ if (iflags & ITEM_LOCAL_CHANGE)
+ iflags |= symlink_timeset_failed_flags;
+ } else if (keep_time
+ ? cmp_time(file->modtime, sxp->st.st_mtime) != 0
+ : iflags & (ITEM_TRANSFER|ITEM_LOCAL_CHANGE) && !(iflags & ITEM_MATCHED)
+ && (!(iflags & ITEM_XNAME_FOLLOWS) || *xname))
+ iflags |= ITEM_REPORT_TIME;
+#if !defined HAVE_LCHMOD && !defined HAVE_SETATTRLIST
+ if (S_ISLNK(file->mode)) {
+ ;
+ } else
+#endif
+ if (preserve_perms) {
+ if (!BITS_EQUAL(sxp->st.st_mode, file->mode, CHMOD_BITS))
+ iflags |= ITEM_REPORT_PERMS;
+ } else if (preserve_executability
+ && ((sxp->st.st_mode & 0111 ? 1 : 0) ^ (file->mode & 0111 ? 1 : 0)))
+ iflags |= ITEM_REPORT_PERMS;
+ if (uid_ndx && am_root && (uid_t)F_OWNER(file) != sxp->st.st_uid)
+ iflags |= ITEM_REPORT_OWNER;
+ if (gid_ndx && !(file->flags & FLAG_SKIP_GROUP)
+ && sxp->st.st_gid != (gid_t)F_GROUP(file))
+ iflags |= ITEM_REPORT_GROUP;
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && !S_ISLNK(file->mode)) {
+ if (!ACL_READY(*sxp))
+ get_acl(fnamecmp, sxp);
+ if (set_acl(NULL, file, sxp, file->mode))
+ iflags |= ITEM_REPORT_ACL;
+ }
+#endif
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs) {
+ if (!XATTR_READY(*sxp))
+ get_xattr(fnamecmp, sxp);
+ if (xattr_diff(file, sxp, 1))
+ iflags |= ITEM_REPORT_XATTR;
+ }
+#endif
+ } else {
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs && xattr_diff(file, NULL, 1))
+ iflags |= ITEM_REPORT_XATTR;
+#endif
+ iflags |= ITEM_IS_NEW;
+ }
+
+ iflags &= 0xffff;
+ if ((iflags & (SIGNIFICANT_ITEM_FLAGS|ITEM_REPORT_XATTR) || INFO_GTE(NAME, 2)
+ || stdout_format_has_i > 1 || (xname && *xname)) && !read_batch) {
+ if (protocol_version >= 29) {
+ if (ndx >= 0)
+ write_ndx(sock_f_out, ndx);
+ write_shortint(sock_f_out, iflags);
+ if (iflags & ITEM_BASIS_TYPE_FOLLOWS)
+ write_byte(sock_f_out, fnamecmp_type);
+ if (iflags & ITEM_XNAME_FOLLOWS)
+ write_vstring(sock_f_out, xname, strlen(xname));
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs && do_xfers
+ && iflags & (ITEM_REPORT_XATTR|ITEM_TRANSFER)) {
+ int fd = iflags & ITEM_REPORT_XATTR
+ && !(want_xattr_optim && BITS_SET(iflags, ITEM_XNAME_FOLLOWS|ITEM_LOCAL_CHANGE))
+ ? sock_f_out : -1;
+ send_xattr_request(NULL, file, fd);
+ }
+#endif
+ } else if (ndx >= 0) {
+ enum logcode code = logfile_format_has_i ? FINFO : FCLIENT;
+ log_item(code, file, iflags, xname);
+ }
+ }
+}
+
+
+/* Perform our quick-check heuristic for determining if a file is unchanged. */
+int unchanged_file(char *fn, struct file_struct *file, STRUCT_STAT *st)
+{
+ if (st->st_size != F_LENGTH(file))
+ return 0;
+
+ /* if always checksum is set then we use the checksum instead
+ of the file time to determine whether to sync */
+ if (always_checksum > 0 && S_ISREG(st->st_mode)) {
+ char sum[MAX_DIGEST_LEN];
+ file_checksum(fn, st, sum);
+ return memcmp(sum, F_SUM(file), checksum_len) == 0;
+ }
+
+ if (size_only > 0)
+ return 1;
+
+ if (ignore_times)
+ return 0;
+
+ return cmp_time(st->st_mtime, file->modtime) == 0;
+}
+
+
+/*
+ * set (initialize) the size entries in the per-file sum_struct
+ * calculating dynamic block and checksum sizes.
+ *
+ * This is only called from generate_and_send_sums() but is a separate
+ * function to encapsulate the logic.
+ *
+ * The block size is a rounded square root of file length.
+ *
+ * The checksum size is determined according to:
+ * blocksum_bits = BLOCKSUM_BIAS + 2*log2(file_len) - log2(block_len)
+ * provided by Donovan Baarda which gives a probability of rsync
+ * algorithm corrupting data and falling back using the whole md4
+ * checksums.
+ *
+ * This might be made one of several selectable heuristics.
+ */
+static void sum_sizes_sqroot(struct sum_struct *sum, int64 len)
+{
+ int32 blength;
+ int s2length;
+ int64 l;
+
+ if (len < 0) {
+ /* The file length overflowed our int64 var, so we can't process this file. */
+ sum->count = -1; /* indicate overflow error */
+ return;
+ }
+
+ if (block_size)
+ blength = block_size;
+ else if (len <= BLOCK_SIZE * BLOCK_SIZE)
+ blength = BLOCK_SIZE;
+ else {
+ int32 max_blength = protocol_version < 30 ? OLD_MAX_BLOCK_SIZE : MAX_BLOCK_SIZE;
+ int32 c;
+ int cnt;
+ for (c = 1, l = len, cnt = 0; l >>= 2; c <<= 1, cnt++) {}
+ if (c < 0 || c >= max_blength)
+ blength = max_blength;
+ else {
+ blength = 0;
+ do {
+ blength |= c;
+ if (len < (int64)blength * blength)
+ blength &= ~c;
+ c >>= 1;
+ } while (c >= 8); /* round to multiple of 8 */
+ blength = MAX(blength, BLOCK_SIZE);
+ }
+ }
+
+ if (protocol_version < 27) {
+ s2length = csum_length;
+ } else if (csum_length == SUM_LENGTH) {
+ s2length = SUM_LENGTH;
+ } else {
+ int32 c;
+ int b = BLOCKSUM_BIAS;
+ for (l = len; l >>= 1; b += 2) {}
+ for (c = blength; (c >>= 1) && b; b--) {}
+ /* add a bit, subtract rollsum, round up. */
+ s2length = (b + 1 - 32 + 7) / 8; /* --optimize in compiler-- */
+ s2length = MAX(s2length, csum_length);
+ s2length = MIN(s2length, SUM_LENGTH);
+ }
+
+ sum->flength = len;
+ sum->blength = blength;
+ sum->s2length = s2length;
+ sum->remainder = (int32)(len % blength);
+ sum->count = (int32)(l = (len / blength) + (sum->remainder != 0));
+
+ if ((int64)sum->count != l)
+ sum->count = -1;
+
+ if (sum->count && DEBUG_GTE(DELTASUM, 2)) {
+ rprintf(FINFO,
+ "count=%s rem=%ld blength=%ld s2length=%d flength=%s\n",
+ big_num(sum->count), (long)sum->remainder, (long)sum->blength,
+ sum->s2length, big_num(sum->flength));
+ }
+}
+
+
+/*
+ * Generate and send a stream of signatures/checksums that describe a buffer
+ *
+ * Generate approximately one checksum every block_len bytes.
+ */
+static int generate_and_send_sums(int fd, OFF_T len, int f_out, int f_copy)
+{
+ int32 i;
+ struct map_struct *mapbuf;
+ struct sum_struct sum;
+ OFF_T offset = 0;
+
+ sum_sizes_sqroot(&sum, len);
+ if (sum.count < 0)
+ return -1;
+ write_sum_head(f_out, &sum);
+
+ if (append_mode > 0 && f_copy < 0)
+ return 0;
+
+ if (len > 0)
+ mapbuf = map_file(fd, len, MAX_MAP_SIZE, sum.blength);
+ else
+ mapbuf = NULL;
+
+ for (i = 0; i < sum.count; i++) {
+ int32 n1 = (int32)MIN(len, (OFF_T)sum.blength);
+ char *map = map_ptr(mapbuf, offset, n1);
+ char sum2[SUM_LENGTH];
+ uint32 sum1;
+
+ len -= n1;
+ offset += n1;
+
+ if (f_copy >= 0) {
+ full_write(f_copy, map, n1);
+ if (append_mode > 0)
+ continue;
+ }
+
+ sum1 = get_checksum1(map, n1);
+ get_checksum2(map, n1, sum2);
+
+ if (DEBUG_GTE(DELTASUM, 3)) {
+ rprintf(FINFO,
+ "chunk[%s] offset=%s len=%ld sum1=%08lx\n",
+ big_num(i), big_num(offset - n1), (long)n1,
+ (unsigned long)sum1);
+ }
+ write_int(f_out, sum1);
+ write_buf(f_out, sum2, sum.s2length);
+ }
+
+ if (mapbuf)
+ unmap_file(mapbuf);
+
+ return 0;
+}
+
+
+/* Try to find a filename in the same dir as "fname" with a similar name. */
+static struct file_struct *find_fuzzy(struct file_struct *file, struct file_list *dirlist_array[], uchar *fnamecmp_type_ptr)
+{
+ int fname_len, fname_suf_len;
+ const char *fname_suf, *fname = file->basename;
+ uint32 lowest_dist = 25 << 16; /* ignore a distance greater than 25 */
+ int i, j;
+ struct file_struct *lowest_fp = NULL;
+
+ fname_len = strlen(fname);
+ fname_suf = find_filename_suffix(fname, fname_len, &fname_suf_len);
+
+ /* Try to find an exact size+mtime match first. */
+ for (i = 0; i < fuzzy_basis; i++) {
+ struct file_list *dirlist = dirlist_array[i];
+
+ if (!dirlist)
+ continue;
+
+ for (j = 0; j < dirlist->used; j++) {
+ struct file_struct *fp = dirlist->files[j];
+
+ if (!F_IS_ACTIVE(fp))
+ continue;
+
+ if (!S_ISREG(fp->mode) || !F_LENGTH(fp) || fp->flags & FLAG_FILE_SENT)
+ continue;
+
+ if (F_LENGTH(fp) == F_LENGTH(file) && cmp_time(fp->modtime, file->modtime) == 0) {
+ if (DEBUG_GTE(FUZZY, 2))
+ rprintf(FINFO, "fuzzy size/modtime match for %s\n", f_name(fp, NULL));
+ *fnamecmp_type_ptr = FNAMECMP_FUZZY + i;
+ return fp;
+ }
+
+ }
+ }
+
+ for (i = 0; i < fuzzy_basis; i++) {
+ struct file_list *dirlist = dirlist_array[i];
+
+ if (!dirlist)
+ continue;
+
+ for (j = 0; j < dirlist->used; j++) {
+ struct file_struct *fp = dirlist->files[j];
+ const char *suf, *name;
+ int len, suf_len;
+ uint32 dist;
+
+ if (!F_IS_ACTIVE(fp))
+ continue;
+
+ if (!S_ISREG(fp->mode) || !F_LENGTH(fp) || fp->flags & FLAG_FILE_SENT)
+ continue;
+
+ name = fp->basename;
+ len = strlen(name);
+ suf = find_filename_suffix(name, len, &suf_len);
+
+ dist = fuzzy_distance(name, len, fname, fname_len);
+ /* Add some extra weight to how well the suffixes match. */
+ dist += fuzzy_distance(suf, suf_len, fname_suf, fname_suf_len) * 10;
+ if (DEBUG_GTE(FUZZY, 2)) {
+ rprintf(FINFO, "fuzzy distance for %s = %d.%05d\n",
+ f_name(fp, NULL), (int)(dist>>16), (int)(dist&0xFFFF));
+ }
+ if (dist <= lowest_dist) {
+ lowest_dist = dist;
+ lowest_fp = fp;
+ *fnamecmp_type_ptr = FNAMECMP_FUZZY + i;
+ }
+ }
+ }
+
+ return lowest_fp;
+}
+
+/* Copy a file found in our --copy-dest handling. */
+static int copy_altdest_file(const char *src, const char *dest, struct file_struct *file)
+{
+ char buf[MAXPATHLEN];
+ const char *copy_to, *partialptr;
+ int save_preserve_xattrs = preserve_xattrs;
+ int ok, fd_w;
+
+ if (inplace) {
+ /* Let copy_file open the destination in place. */
+ fd_w = -1;
+ copy_to = dest;
+ } else {
+ fd_w = open_tmpfile(buf, dest, file);
+ if (fd_w < 0)
+ return -1;
+ copy_to = buf;
+ }
+ cleanup_set(copy_to, NULL, NULL, -1, -1);
+ if (copy_file(src, copy_to, fd_w, file->mode) < 0) {
+ if (INFO_GTE(COPY, 1)) {
+ rsyserr(FINFO, errno, "copy_file %s => %s",
+ full_fname(src), copy_to);
+ }
+ /* Try to clean up. */
+ unlink(copy_to);
+ cleanup_disable();
+ return -1;
+ }
+ partialptr = partial_dir ? partial_dir_fname(dest) : NULL;
+ preserve_xattrs = 0; /* xattrs were copied with file */
+ ok = finish_transfer(dest, copy_to, src, partialptr, file, 1, 0);
+ preserve_xattrs = save_preserve_xattrs;
+ cleanup_disable();
+ return ok ? 0 : -1;
+}
+
+/* This is only called for regular files. We return -2 if we've finished
+ * handling the file, -1 if no dest-linking occurred, or a non-negative
+ * value if we found an alternate basis file. If we're called with the
+ * find_exact_for_existing flag, the destination file already exists, so
+ * we only try to find an exact alt-dest match. In this case, the returns
+ * are only -2 & -1 (both as above). */
+static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
+ char *cmpbuf, stat_x *sxp, int find_exact_for_existing,
+ int itemizing, enum logcode code)
+{
+ STRUCT_STAT real_st = sxp->st;
+ int best_match = -1;
+ int match_level = 0;
+ int j = 0;
+
+ do {
+ pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname);
+ if (link_stat(cmpbuf, &sxp->st, 0) < 0 || !S_ISREG(sxp->st.st_mode))
+ continue;
+ switch (match_level) {
+ case 0:
+ best_match = j;
+ match_level = 1;
+ /* FALL THROUGH */
+ case 1:
+ if (!unchanged_file(cmpbuf, file, &sxp->st))
+ continue;
+ best_match = j;
+ match_level = 2;
+ /* FALL THROUGH */
+ case 2:
+ if (!unchanged_attrs(cmpbuf, file, sxp)) {
+ free_stat_x(sxp);
+ continue;
+ }
+ best_match = j;
+ match_level = 3;
+ break;
+ }
+ break;
+ } while (basis_dir[++j] != NULL);
+
+ if (!match_level)
+ return -1;
+
+ if (j != best_match) {
+ j = best_match;
+ pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname);
+ if (link_stat(cmpbuf, &sxp->st, 0) < 0)
+ return -1;
+ }
+
+ if (match_level == 3 && !copy_dest) {
+ if (find_exact_for_existing) {
+ if (link_dest && real_st.st_dev == sxp->st.st_dev && real_st.st_ino == sxp->st.st_ino)
+ return -1;
+ if (do_unlink(fname) < 0 && errno != ENOENT) {
+ sxp->st = real_st;
+ return -1;
+ }
+ }
+#ifdef SUPPORT_HARD_LINKS
+ if (link_dest) {
+ if (!hard_link_one(file, fname, cmpbuf, 1))
+ goto try_a_copy;
+ if (preserve_hard_links && F_IS_HLINKED(file))
+ finish_hard_link(file, fname, ndx, &sxp->st, itemizing, code, j);
+ if (!maybe_ATTRS_REPORT && (INFO_GTE(NAME, 2) || stdout_format_has_i > 1)) {
+ itemize(cmpbuf, file, ndx, 1, sxp,
+ ITEM_LOCAL_CHANGE | ITEM_XNAME_FOLLOWS,
+ 0, "");
+ }
+ } else
+#endif
+ {
+ if (itemizing)
+ itemize(cmpbuf, file, ndx, 0, sxp, 0, 0, NULL);
+ }
+ if (INFO_GTE(NAME, 2) && maybe_ATTRS_REPORT)
+ rprintf(FCLIENT, "%s is uptodate\n", fname);
+ return -2;
+ }
+
+ if (find_exact_for_existing) {
+ sxp->st = real_st;
+ return -1;
+ }
+
+ if (match_level >= 2) {
+#ifdef SUPPORT_HARD_LINKS
+ try_a_copy: /* Copy the file locally. */
+#endif
+ if (!dry_run && copy_altdest_file(cmpbuf, fname, file) < 0) {
+ if (find_exact_for_existing) /* Can get here via hard-link failure */
+ sxp->st = real_st;
+ return -1;
+ }
+ if (itemizing)
+ itemize(cmpbuf, file, ndx, 0, sxp, ITEM_LOCAL_CHANGE, 0, NULL);
+ if (maybe_ATTRS_REPORT
+ && ((!itemizing && INFO_GTE(NAME, 1) && match_level == 2)
+ || (INFO_GTE(NAME, 2) && match_level == 3))) {
+ code = match_level == 3 ? FCLIENT : FINFO;
+ rprintf(code, "%s%s\n", fname,
+ match_level == 3 ? " is uptodate" : "");
+ }
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && F_IS_HLINKED(file))
+ finish_hard_link(file, fname, ndx, &sxp->st, itemizing, code, -1);
+#endif
+ return -2;
+ }
+
+ return FNAMECMP_BASIS_DIR_LOW + j;
+}
+
+/* This is only called for non-regular files. We return -2 if we've finished
+ * handling the file, or -1 if no dest-linking occurred, or a non-negative
+ * value if we found an alternate basis file. */
+static int try_dests_non(struct file_struct *file, char *fname, int ndx,
+ char *cmpbuf, stat_x *sxp, int itemizing,
+ enum logcode code)
+{
+ int best_match = -1;
+ int match_level = 0;
+ enum nonregtype type;
+ uint32 *devp;
+#ifdef SUPPORT_LINKS
+ char lnk[MAXPATHLEN];
+ int len;
+#endif
+ int j = 0;
+
+#ifndef SUPPORT_LINKS
+ if (S_ISLNK(file->mode))
+ return -1;
+#endif
+ if (S_ISDIR(file->mode)) {
+ type = TYPE_DIR;
+ } else if (IS_SPECIAL(file->mode))
+ type = TYPE_SPECIAL;
+ else if (IS_DEVICE(file->mode))
+ type = TYPE_DEVICE;
+#ifdef SUPPORT_LINKS
+ else if (S_ISLNK(file->mode))
+ type = TYPE_SYMLINK;
+#endif
+ else {
+ rprintf(FERROR,
+ "internal: try_dests_non() called with invalid mode (%o)\n",
+ (int)file->mode);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+
+ do {
+ pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname);
+ if (link_stat(cmpbuf, &sxp->st, 0) < 0)
+ continue;
+ switch (type) {
+ case TYPE_DIR:
+ if (!S_ISDIR(sxp->st.st_mode))
+ continue;
+ break;
+ case TYPE_SPECIAL:
+ if (!IS_SPECIAL(sxp->st.st_mode))
+ continue;
+ break;
+ case TYPE_DEVICE:
+ if (!IS_DEVICE(sxp->st.st_mode))
+ continue;
+ break;
+ case TYPE_SYMLINK:
+#ifdef SUPPORT_LINKS
+ if (!S_ISLNK(sxp->st.st_mode))
+ continue;
+ break;
+#else
+ return -1;
+#endif
+ }
+ if (match_level < 1) {
+ match_level = 1;
+ best_match = j;
+ }
+ switch (type) {
+ case TYPE_DIR:
+ case TYPE_SPECIAL:
+ break;
+ case TYPE_DEVICE:
+ devp = F_RDEV_P(file);
+ if (sxp->st.st_rdev != MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp)))
+ continue;
+ break;
+ case TYPE_SYMLINK:
+#ifdef SUPPORT_LINKS
+ if ((len = do_readlink(cmpbuf, lnk, MAXPATHLEN-1)) <= 0)
+ continue;
+ lnk[len] = '\0';
+ if (strcmp(lnk, F_SYMLINK(file)) != 0)
+ continue;
+ break;
+#else
+ return -1;
+#endif
+ }
+ if (match_level < 2) {
+ match_level = 2;
+ best_match = j;
+ }
+ if (unchanged_attrs(cmpbuf, file, sxp)) {
+ match_level = 3;
+ best_match = j;
+ break;
+ }
+ } while (basis_dir[++j] != NULL);
+
+ if (!match_level)
+ return -1;
+
+ if (j != best_match) {
+ j = best_match;
+ pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname);
+ if (link_stat(cmpbuf, &sxp->st, 0) < 0)
+ return -1;
+ }
+
+ if (match_level == 3) {
+#ifdef SUPPORT_HARD_LINKS
+ if (link_dest
+#ifndef CAN_HARDLINK_SYMLINK
+ && !S_ISLNK(file->mode)
+#endif
+#ifndef CAN_HARDLINK_SPECIAL
+ && !IS_SPECIAL(file->mode) && !IS_DEVICE(file->mode)
+#endif
+ && !S_ISDIR(file->mode)) {
+ if (do_link(cmpbuf, fname) < 0) {
+ rsyserr(FERROR_XFER, errno,
+ "failed to hard-link %s with %s",
+ cmpbuf, fname);
+ return j;
+ }
+ if (preserve_hard_links && F_IS_HLINKED(file))
+ finish_hard_link(file, fname, ndx, NULL, itemizing, code, -1);
+ } else
+#endif
+ match_level = 2;
+ if (itemizing && stdout_format_has_i
+ && (INFO_GTE(NAME, 2) || stdout_format_has_i > 1)) {
+ int chg = compare_dest && type != TYPE_DIR ? 0
+ : ITEM_LOCAL_CHANGE + (match_level == 3 ? ITEM_XNAME_FOLLOWS : 0);
+ char *lp = match_level == 3 ? "" : NULL;
+ itemize(cmpbuf, file, ndx, 0, sxp, chg + ITEM_MATCHED, 0, lp);
+ }
+ if (INFO_GTE(NAME, 2) && maybe_ATTRS_REPORT) {
+ rprintf(FCLIENT, "%s%s is uptodate\n",
+ fname, type == TYPE_DIR ? "/" : "");
+ }
+ return -2;
+ }
+
+ return j;
+}
+
+static void list_file_entry(struct file_struct *f)
+{
+ char permbuf[PERMSTRING_SIZE];
+ int64 len;
+ int colwidth = human_readable ? 14 : 11;
+
+ if (!F_IS_ACTIVE(f)) {
+ /* this can happen if duplicate names were removed */
+ return;
+ }
+
+ permstring(permbuf, f->mode);
+ len = F_LENGTH(f);
+
+ /* TODO: indicate '+' if the entry has an ACL. */
+
+#ifdef SUPPORT_LINKS
+ if (preserve_links && S_ISLNK(f->mode)) {
+ rprintf(FINFO, "%s %*s %s %s -> %s\n",
+ permbuf, colwidth, human_num(len),
+ timestring(f->modtime), f_name(f, NULL),
+ F_SYMLINK(f));
+ } else
+#endif
+ if (missing_args == 2 && f->mode == 0) {
+ rprintf(FINFO, "%-*s %s\n",
+ colwidth + 31, "*missing",
+ f_name(f, NULL));
+ } else {
+ rprintf(FINFO, "%s %*s %s %s\n",
+ permbuf, colwidth, human_num(len),
+ timestring(f->modtime), f_name(f, NULL));
+ }
+}
+
+static int phase = 0;
+static int dflt_perms;
+
+static int implied_dirs_are_missing;
+/* Helper for recv_generator's skip_dir and dry_missing_dir tests. */
+static BOOL is_below(struct file_struct *file, struct file_struct *subtree)
+{
+ return F_DEPTH(file) > F_DEPTH(subtree)
+ && (!implied_dirs_are_missing || f_name_has_prefix(file, subtree));
+}
+
+/* Acts on the indicated item in cur_flist whose name is fname. If a dir,
+ * make sure it exists, and has the right permissions/timestamp info. For
+ * all other non-regular files (symlinks, etc.) we create them here. For
+ * regular files that have changed, we try to find a basis file and then
+ * start sending checksums. The ndx is the file's unique index value.
+ *
+ * The fname parameter must point to a MAXPATHLEN buffer! (e.g it gets
+ * passed to delete_item(), which can use it during a recursive delete.)
+ *
+ * Note that f_out is set to -1 when doing final directory-permission and
+ * modification-time repair. */
+static void recv_generator(char *fname, struct file_struct *file, int ndx,
+ int itemizing, enum logcode code, int f_out)
+{
+ static const char *parent_dirname = "";
+ /* Missing dir not created due to --dry-run; will still be scanned. */
+ static struct file_struct *dry_missing_dir = NULL;
+ /* Missing dir whose contents are skipped altogether due to
+ * --ignore-non-existing, daemon exclude, or mkdir failure. */
+ static struct file_struct *skip_dir = NULL;
+ static struct file_list *fuzzy_dirlist[MAX_BASIS_DIRS+1];
+ static int need_fuzzy_dirlist = 0;
+ struct file_struct *fuzzy_file = NULL;
+ int fd = -1, f_copy = -1;
+ stat_x sx, real_sx;
+ STRUCT_STAT partial_st;
+ struct file_struct *back_file = NULL;
+ int statret, real_ret, stat_errno;
+ char *fnamecmp, *partialptr, *backupptr = NULL;
+ char fnamecmpbuf[MAXPATHLEN];
+ uchar fnamecmp_type;
+ int del_opts = delete_mode || force_delete ? DEL_RECURSE : 0;
+ int is_dir = !S_ISDIR(file->mode) ? 0
+ : inc_recurse && ndx != cur_flist->ndx_start - 1 ? -1
+ : 1;
+
+ if (DEBUG_GTE(GENR, 1))
+ rprintf(FINFO, "recv_generator(%s,%d)\n", fname, ndx);
+
+ if (list_only) {
+ if (is_dir < 0
+ || (is_dir && !implied_dirs && file->flags & FLAG_IMPLIED_DIR))
+ return;
+ list_file_entry(file);
+ return;
+ }
+
+ if (skip_dir) {
+ if (is_below(file, skip_dir)) {
+ if (is_dir)
+ file->flags |= FLAG_MISSING_DIR;
+#ifdef SUPPORT_HARD_LINKS
+ else if (F_IS_HLINKED(file))
+ handle_skipped_hlink(file, itemizing, code, f_out);
+#endif
+ return;
+ }
+ skip_dir = NULL;
+ }
+
+ init_stat_x(&sx);
+ if (daemon_filter_list.head && (*fname != '.' || fname[1])) {
+ if (check_filter(&daemon_filter_list, FLOG, fname, is_dir) < 0) {
+ if (is_dir < 0)
+ return;
+#ifdef SUPPORT_HARD_LINKS
+ if (F_IS_HLINKED(file))
+ handle_skipped_hlink(file, itemizing, code, f_out);
+#endif
+ rprintf(FERROR_XFER,
+ "ERROR: daemon refused to receive %s \"%s\"\n",
+ is_dir ? "directory" : "file", fname);
+ if (is_dir)
+ goto skipping_dir_contents;
+ return;
+ }
+ }
+
+ if (dry_run > 1 || (dry_missing_dir && is_below(file, dry_missing_dir))) {
+ int i;
+ parent_is_dry_missing:
+ for (i = 0; i < fuzzy_basis; i++) {
+ if (fuzzy_dirlist[i]) {
+ flist_free(fuzzy_dirlist[i]);
+ fuzzy_dirlist[i] = NULL;
+ }
+ }
+ parent_dirname = "";
+ statret = -1;
+ stat_errno = ENOENT;
+ } else {
+ const char *dn = file->dirname ? file->dirname : ".";
+ dry_missing_dir = NULL;
+ if (parent_dirname != dn && strcmp(parent_dirname, dn) != 0) {
+ if (relative_paths && !implied_dirs
+ && do_stat(dn, &sx.st) < 0) {
+ if (dry_run)
+ goto parent_is_dry_missing;
+ if (make_path(fname, MKP_DROP_NAME | MKP_SKIP_SLASH) < 0) {
+ rsyserr(FERROR_XFER, errno,
+ "recv_generator: mkdir %s failed",
+ full_fname(dn));
+ }
+ }
+ if (fuzzy_basis) {
+ int i;
+ for (i = 0; i < fuzzy_basis; i++) {
+ if (fuzzy_dirlist[i]) {
+ flist_free(fuzzy_dirlist[i]);
+ fuzzy_dirlist[i] = NULL;
+ }
+ }
+ need_fuzzy_dirlist = 1;
+ }
+#ifdef SUPPORT_ACLS
+ if (!preserve_perms)
+ dflt_perms = default_perms_for_dir(dn);
+#endif
+ }
+ parent_dirname = dn;
+
+ if (need_fuzzy_dirlist && S_ISREG(file->mode)) {
+ int i;
+ strlcpy(fnamecmpbuf, dn, sizeof fnamecmpbuf);
+ for (i = 0; i < fuzzy_basis; i++) {
+ if (i && pathjoin(fnamecmpbuf, MAXPATHLEN, basis_dir[i-1], dn) >= MAXPATHLEN)
+ continue;
+ fuzzy_dirlist[i] = get_dirlist(fnamecmpbuf, -1, GDL_IGNORE_FILTER_RULES);
+ if (fuzzy_dirlist[i] && fuzzy_dirlist[i]->used == 0) {
+ flist_free(fuzzy_dirlist[i]);
+ fuzzy_dirlist[i] = NULL;
+ }
+ }
+ need_fuzzy_dirlist = 0;
+ }
+
+ statret = link_stat(fname, &sx.st, keep_dirlinks && is_dir);
+ stat_errno = errno;
+ }
+
+ if (missing_args == 2 && file->mode == 0) {
+ if (filter_list.head && check_filter(&filter_list, FINFO, fname, is_dir) < 0)
+ return;
+ if (statret == 0)
+ delete_item(fname, sx.st.st_mode, del_opts);
+ return;
+ }
+
+ if (ignore_non_existing > 0 && statret == -1 && stat_errno == ENOENT) {
+ if (is_dir) {
+ if (is_dir < 0)
+ return;
+ skip_dir = file;
+ file->flags |= FLAG_MISSING_DIR;
+ }
+#ifdef SUPPORT_HARD_LINKS
+ else if (F_IS_HLINKED(file))
+ handle_skipped_hlink(file, itemizing, code, f_out);
+#endif
+ if (INFO_GTE(SKIP, 1)) {
+ rprintf(FINFO, "not creating new %s \"%s\"\n",
+ is_dir ? "directory" : "file", fname);
+ }
+ return;
+ }
+
+ if (statret == 0 && !(sx.st.st_mode & S_IWUSR)
+ && !am_root && sx.st.st_uid == our_uid)
+ del_opts |= DEL_NO_UID_WRITE;
+
+ if (ignore_existing > 0 && statret == 0
+ && (!is_dir || !S_ISDIR(sx.st.st_mode))) {
+ if (INFO_GTE(SKIP, 1) && is_dir >= 0)
+ rprintf(FINFO, "%s exists\n", fname);
+#ifdef SUPPORT_HARD_LINKS
+ if (F_IS_HLINKED(file))
+ handle_skipped_hlink(file, itemizing, code, f_out);
+#endif
+ goto cleanup;
+ }
+
+ fnamecmp = fname;
+
+ if (is_dir) {
+ mode_t added_perms;
+ if (!implied_dirs && file->flags & FLAG_IMPLIED_DIR)
+ goto cleanup;
+ if (am_root < 0) {
+ /* For --fake-super, the dir must be useable by the copying
+ * user, just like it would be for root. */
+ added_perms = S_IRUSR|S_IWUSR|S_IXUSR;
+ } else
+ added_perms = 0;
+ if (is_dir < 0) {
+ if (!(preserve_times & PRESERVE_DIR_TIMES))
+ return;
+ /* In inc_recurse mode we want to make sure any missing
+ * directories get created while we're still processing
+ * the parent dir (which allows us to touch the parent
+ * dir's mtime right away). We will handle the dir in
+ * full later (right before we handle its contents). */
+ if (statret == 0
+ && (S_ISDIR(sx.st.st_mode)
+ || delete_item(fname, sx.st.st_mode, del_opts | DEL_FOR_DIR) != 0))
+ goto cleanup; /* Any errors get reported later. */
+ if (do_mkdir(fname, (file->mode|added_perms) & 0700) == 0)
+ file->flags |= FLAG_DIR_CREATED;
+ goto cleanup;
+ }
+ /* The file to be received is a directory, so we need
+ * to prepare appropriately. If there is already a
+ * file of that name and it is *not* a directory, then
+ * we need to delete it. If it doesn't exist, then
+ * (perhaps recursively) create it. */
+ if (statret == 0 && !S_ISDIR(sx.st.st_mode)) {
+ if (delete_item(fname, sx.st.st_mode, del_opts | DEL_FOR_DIR) != 0)
+ goto skipping_dir_contents;
+ statret = -1;
+ }
+ if (dry_run && statret != 0) {
+ if (!dry_missing_dir)
+ dry_missing_dir = file;
+ file->flags |= FLAG_MISSING_DIR;
+ }
+ init_stat_x(&real_sx);
+ real_sx.st = sx.st;
+ real_ret = statret;
+ if (file->flags & FLAG_DIR_CREATED)
+ statret = -1;
+ if (!preserve_perms) { /* See comment in non-dir code below. */
+ file->mode = dest_mode(file->mode, sx.st.st_mode,
+ dflt_perms, statret == 0);
+ }
+ if (statret != 0 && basis_dir[0] != NULL) {
+ int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &sx,
+ itemizing, code);
+ if (j == -2) {
+ itemizing = 0;
+ code = FNONE;
+ statret = 1;
+ } else if (j >= 0) {
+ statret = 1;
+ fnamecmp = fnamecmpbuf;
+ }
+ }
+ if (itemizing && f_out != -1) {
+ itemize(fnamecmp, file, ndx, statret, &sx,
+ statret ? ITEM_LOCAL_CHANGE : 0, 0, NULL);
+ }
+ if (real_ret != 0 && do_mkdir(fname,file->mode|added_perms) < 0 && errno != EEXIST) {
+ if (!relative_paths || errno != ENOENT
+ || make_path(fname, MKP_DROP_NAME | MKP_SKIP_SLASH) < 0
+ || (do_mkdir(fname, file->mode|added_perms) < 0 && errno != EEXIST)) {
+ rsyserr(FERROR_XFER, errno,
+ "recv_generator: mkdir %s failed",
+ full_fname(fname));
+ skipping_dir_contents:
+ rprintf(FERROR,
+ "*** Skipping any contents from this failed directory ***\n");
+ skip_dir = file;
+ file->flags |= FLAG_MISSING_DIR;
+ goto cleanup;
+ }
+ }
+
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs && statret == 1)
+ copy_xattrs(fnamecmpbuf, fname);
+#endif
+ if (set_file_attrs(fname, file, real_ret ? NULL : &real_sx, NULL, 0)
+ && INFO_GTE(NAME, 1) && code != FNONE && f_out != -1)
+ rprintf(code, "%s/\n", fname);
+
+ /* We need to ensure that the dirs in the transfer have both
+ * readable and writable permissions during the time we are
+ * putting files within them. This is then restored to the
+ * former permissions after the transfer is done. */
+#ifdef HAVE_CHMOD
+ if (!am_root && (file->mode & S_IRWXU) != S_IRWXU && dir_tweaking) {
+ mode_t mode = file->mode | S_IRWXU;
+ if (do_chmod(fname, mode) < 0) {
+ rsyserr(FERROR_XFER, errno,
+ "failed to modify permissions on %s",
+ full_fname(fname));
+ }
+ need_retouch_dir_perms = 1;
+ }
+#endif
+
+ if (real_ret != 0 && one_file_system)
+ real_sx.st.st_dev = filesystem_dev;
+ if (inc_recurse) {
+ if (one_file_system) {
+ uint32 *devp = F_DIR_DEV_P(file);
+ DEV_MAJOR(devp) = major(real_sx.st.st_dev);
+ DEV_MINOR(devp) = minor(real_sx.st.st_dev);
+ }
+ }
+ else if (delete_during && f_out != -1 && !phase
+ && !(file->flags & FLAG_MISSING_DIR)) {
+ if (file->flags & FLAG_CONTENT_DIR)
+ delete_in_dir(fname, file, &real_sx.st.st_dev);
+ else
+ change_local_filter_dir(fname, strlen(fname), F_DEPTH(file));
+ }
+ goto cleanup;
+ }
+
+ /* If we're not preserving permissions, change the file-list's
+ * mode based on the local permissions and some heuristics. */
+ if (!preserve_perms) {
+ int exists = statret == 0 && !S_ISDIR(sx.st.st_mode);
+ file->mode = dest_mode(file->mode, sx.st.st_mode, dflt_perms,
+ exists);
+ }
+
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && F_HLINK_NOT_FIRST(file)
+ && hard_link_check(file, ndx, fname, statret, &sx, itemizing, code))
+ goto cleanup;
+#endif
+
+ if (preserve_links && S_ISLNK(file->mode)) {
+#ifdef SUPPORT_LINKS
+ const char *sl = F_SYMLINK(file);
+ if (safe_symlinks && unsafe_symlink(sl, fname)) {
+ if (INFO_GTE(NAME, 1)) {
+ if (solo_file) {
+ /* fname contains the destination path, but we
+ * want to report the source path. */
+ fname = f_name(file, NULL);
+ }
+ rprintf(FINFO,
+ "ignoring unsafe symlink \"%s\" -> \"%s\"\n",
+ fname, sl);
+ }
+ return;
+ }
+ if (statret == 0) {
+ char lnk[MAXPATHLEN];
+ int len;
+
+ if (S_ISLNK(sx.st.st_mode)
+ && (len = do_readlink(fname, lnk, MAXPATHLEN-1)) > 0
+ && strncmp(lnk, sl, len) == 0 && sl[len] == '\0') {
+ /* The link is pointing to the right place. */
+ set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT);
+ if (itemizing)
+ itemize(fname, file, ndx, 0, &sx, 0, 0, NULL);
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && F_IS_HLINKED(file))
+ finish_hard_link(file, fname, ndx, &sx.st, itemizing, code, -1);
+#endif
+ if (remove_source_files == 1)
+ goto return_with_success;
+ goto cleanup;
+ }
+ } else if (basis_dir[0] != NULL) {
+ int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &sx,
+ itemizing, code);
+ if (j == -2) {
+#ifndef CAN_HARDLINK_SYMLINK
+ if (link_dest) {
+ /* Resort to --copy-dest behavior. */
+ } else
+#endif
+ if (!copy_dest)
+ goto cleanup;
+ itemizing = 0;
+ code = FNONE;
+ } else if (j >= 0) {
+ statret = 1;
+ fnamecmp = fnamecmpbuf;
+ }
+ }
+ if (atomic_create(file, fname, sl, NULL, MAKEDEV(0, 0), &sx, statret == 0 ? DEL_FOR_SYMLINK : 0)) {
+ set_file_attrs(fname, file, NULL, NULL, 0);
+ if (itemizing) {
+ if (statret == 0 && !S_ISLNK(sx.st.st_mode))
+ statret = -1;
+ itemize(fnamecmp, file, ndx, statret, &sx,
+ ITEM_LOCAL_CHANGE|ITEM_REPORT_CHANGE, 0, NULL);
+ }
+ if (code != FNONE && INFO_GTE(NAME, 1))
+ rprintf(code, "%s -> %s\n", fname, sl);
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && F_IS_HLINKED(file))
+ finish_hard_link(file, fname, ndx, NULL, itemizing, code, -1);
+#endif
+ /* This does not check remove_source_files == 1
+ * because this is one of the items that the old
+ * --remove-sent-files option would remove. */
+ if (remove_source_files)
+ goto return_with_success;
+ }
+#endif
+ goto cleanup;
+ }
+
+ if ((am_root && preserve_devices && IS_DEVICE(file->mode))
+ || (preserve_specials && IS_SPECIAL(file->mode))) {
+ dev_t rdev;
+ int del_for_flag = 0;
+ if (IS_DEVICE(file->mode)) {
+ uint32 *devp = F_RDEV_P(file);
+ rdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
+ } else
+ rdev = 0;
+ if (statret == 0) {
+ if (IS_DEVICE(file->mode)) {
+ if (!IS_DEVICE(sx.st.st_mode))
+ statret = -1;
+ del_for_flag = DEL_FOR_DEVICE;
+ } else {
+ if (!IS_SPECIAL(sx.st.st_mode))
+ statret = -1;
+ del_for_flag = DEL_FOR_SPECIAL;
+ }
+ if (statret == 0
+ && BITS_EQUAL(sx.st.st_mode, file->mode, _S_IFMT)
+ && (IS_SPECIAL(sx.st.st_mode) || sx.st.st_rdev == rdev)) {
+ /* The device or special file is identical. */
+ set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT);
+ if (itemizing)
+ itemize(fname, file, ndx, 0, &sx, 0, 0, NULL);
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && F_IS_HLINKED(file))
+ finish_hard_link(file, fname, ndx, &sx.st, itemizing, code, -1);
+#endif
+ if (remove_source_files == 1)
+ goto return_with_success;
+ goto cleanup;
+ }
+ } else if (basis_dir[0] != NULL) {
+ int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &sx,
+ itemizing, code);
+ if (j == -2) {
+#ifndef CAN_HARDLINK_SPECIAL
+ if (link_dest) {
+ /* Resort to --copy-dest behavior. */
+ } else
+#endif
+ if (!copy_dest)
+ goto cleanup;
+ itemizing = 0;
+ code = FNONE;
+ } else if (j >= 0) {
+ statret = 1;
+ fnamecmp = fnamecmpbuf;
+ }
+ }
+ if (DEBUG_GTE(GENR, 1)) {
+ rprintf(FINFO, "mknod(%s, 0%o, [%ld,%ld])\n",
+ fname, (int)file->mode,
+ (long)major(rdev), (long)minor(rdev));
+ }
+ if (atomic_create(file, fname, NULL, NULL, rdev, &sx, del_for_flag)) {
+ set_file_attrs(fname, file, NULL, NULL, 0);
+ if (itemizing) {
+ itemize(fnamecmp, file, ndx, statret, &sx,
+ ITEM_LOCAL_CHANGE|ITEM_REPORT_CHANGE, 0, NULL);
+ }
+ if (code != FNONE && INFO_GTE(NAME, 1))
+ rprintf(code, "%s\n", fname);
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && F_IS_HLINKED(file))
+ finish_hard_link(file, fname, ndx, NULL, itemizing, code, -1);
+#endif
+ if (remove_source_files == 1)
+ goto return_with_success;
+ }
+ goto cleanup;
+ }
+
+ if (!S_ISREG(file->mode)) {
+ if (solo_file)
+ fname = f_name(file, NULL);
+ rprintf(FINFO, "skipping non-regular file \"%s\"\n", fname);
+ goto cleanup;
+ }
+
+ if (max_size >= 0 && F_LENGTH(file) > max_size) {
+ if (INFO_GTE(SKIP, 1)) {
+ if (solo_file)
+ fname = f_name(file, NULL);
+ rprintf(FINFO, "%s is over max-size\n", fname);
+ }
+ goto cleanup;
+ }
+ if (min_size >= 0 && F_LENGTH(file) < min_size) {
+ if (INFO_GTE(SKIP, 1)) {
+ if (solo_file)
+ fname = f_name(file, NULL);
+ rprintf(FINFO, "%s is under min-size\n", fname);
+ }
+ goto cleanup;
+ }
+
+ if (update_only > 0 && statret == 0
+ && cmp_time(sx.st.st_mtime, file->modtime) > 0) {
+ if (INFO_GTE(SKIP, 1))
+ rprintf(FINFO, "%s is newer\n", fname);
+#ifdef SUPPORT_HARD_LINKS
+ if (F_IS_HLINKED(file))
+ handle_skipped_hlink(file, itemizing, code, f_out);
+#endif
+ goto cleanup;
+ }
+
+ fnamecmp_type = FNAMECMP_FNAME;
+
+ if (statret == 0 && !S_ISREG(sx.st.st_mode)) {
+ if (delete_item(fname, sx.st.st_mode, del_opts | DEL_FOR_FILE) != 0)
+ goto cleanup;
+ statret = -1;
+ stat_errno = ENOENT;
+ }
+
+ if (basis_dir[0] != NULL && (statret != 0 || !copy_dest)) {
+ int j = try_dests_reg(file, fname, ndx, fnamecmpbuf, &sx,
+ statret == 0, itemizing, code);
+ if (j == -2) {
+ if (remove_source_files == 1)
+ goto return_with_success;
+ goto cleanup;
+ }
+ if (j >= 0) {
+ fnamecmp = fnamecmpbuf;
+ fnamecmp_type = j;
+ statret = 0;
+ }
+ }
+
+ init_stat_x(&real_sx);
+ real_sx.st = sx.st; /* Don't copy xattr/acl pointers, as they would free wrong. */
+ real_ret = statret;
+
+ if (partial_dir && (partialptr = partial_dir_fname(fname)) != NULL
+ && link_stat(partialptr, &partial_st, 0) == 0
+ && S_ISREG(partial_st.st_mode)) {
+ if (statret != 0)
+ goto prepare_to_open;
+ } else
+ partialptr = NULL;
+
+ if (statret != 0 && fuzzy_basis) {
+ /* Sets fnamecmp_type to FNAMECMP_FUZZY or above. */
+ fuzzy_file = find_fuzzy(file, fuzzy_dirlist, &fnamecmp_type);
+ if (fuzzy_file) {
+ f_name(fuzzy_file, fnamecmpbuf);
+ if (DEBUG_GTE(FUZZY, 1)) {
+ rprintf(FINFO, "fuzzy basis selected for %s: %s\n",
+ fname, fnamecmpbuf);
+ }
+ sx.st.st_size = F_LENGTH(fuzzy_file);
+ statret = 0;
+ fnamecmp = fnamecmpbuf;
+ }
+ }
+
+ if (statret != 0) {
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && F_HLINK_NOT_LAST(file)) {
+ cur_flist->in_progress++;
+ goto cleanup;
+ }
+#endif
+ if (stat_errno == ENOENT)
+ goto notify_others;
+ rsyserr(FERROR_XFER, stat_errno, "recv_generator: failed to stat %s",
+ full_fname(fname));
+ goto cleanup;
+ }
+
+ if (fnamecmp_type <= FNAMECMP_BASIS_DIR_HIGH)
+ ;
+ else if (fnamecmp_type == FNAMECMP_FUZZY)
+ ;
+ else if (unchanged_file(fnamecmp, file, &sx.st)) {
+ if (partialptr) {
+ do_unlink(partialptr);
+ handle_partial_dir(partialptr, PDIR_DELETE);
+ }
+ set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT);
+ if (itemizing)
+ itemize(fnamecmp, file, ndx, statret, &sx, 0, 0, NULL);
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && F_IS_HLINKED(file))
+ finish_hard_link(file, fname, ndx, &sx.st, itemizing, code, -1);
+#endif
+ if (remove_source_files != 1)
+ goto cleanup;
+ return_with_success:
+ if (!dry_run)
+ send_msg_int(MSG_SUCCESS, ndx);
+ goto cleanup;
+ }
+
+ if (append_mode > 0 && sx.st.st_size >= F_LENGTH(file)) {
+#ifdef SUPPORT_HARD_LINKS
+ if (F_IS_HLINKED(file))
+ handle_skipped_hlink(file, itemizing, code, f_out);
+#endif
+ goto cleanup;
+ }
+
+ prepare_to_open:
+ if (partialptr) {
+ sx.st = partial_st;
+ fnamecmp = partialptr;
+ fnamecmp_type = FNAMECMP_PARTIAL_DIR;
+ statret = 0;
+ }
+
+ if (!do_xfers)
+ goto notify_others;
+
+ if (read_batch || whole_file) {
+ if (inplace && make_backups > 0 && fnamecmp_type == FNAMECMP_FNAME) {
+ if (!(backupptr = get_backup_name(fname)))
+ goto cleanup;
+ if (!(back_file = make_file(fname, NULL, NULL, 0, NO_FILTERS)))
+ goto pretend_missing;
+ if (copy_file(fname, backupptr, -1, back_file->mode) < 0) {
+ unmake_file(back_file);
+ back_file = NULL;
+ goto cleanup;
+ }
+ }
+ goto notify_others;
+ }
+
+ if (fuzzy_dirlist[0]) {
+ int j = flist_find(fuzzy_dirlist[0], file);
+ if (j >= 0) /* don't use changing file as future fuzzy basis */
+ fuzzy_dirlist[0]->files[j]->flags |= FLAG_FILE_SENT;
+ }
+
+ /* open the file */
+ if ((fd = do_open(fnamecmp, O_RDONLY, 0)) < 0) {
+ rsyserr(FERROR, errno, "failed to open %s, continuing",
+ full_fname(fnamecmp));
+ pretend_missing:
+ /* pretend the file didn't exist */
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && F_HLINK_NOT_LAST(file)) {
+ cur_flist->in_progress++;
+ goto cleanup;
+ }
+#endif
+ statret = real_ret = -1;
+ goto notify_others;
+ }
+
+ if (inplace && make_backups > 0 && fnamecmp_type == FNAMECMP_FNAME) {
+ if (!(backupptr = get_backup_name(fname))) {
+ close(fd);
+ goto cleanup;
+ }
+ if (!(back_file = make_file(fname, NULL, NULL, 0, NO_FILTERS))) {
+ close(fd);
+ goto pretend_missing;
+ }
+ if (robust_unlink(backupptr) && errno != ENOENT) {
+ rsyserr(FERROR_XFER, errno, "unlink %s",
+ full_fname(backupptr));
+ unmake_file(back_file);
+ back_file = NULL;
+ close(fd);
+ goto cleanup;
+ }
+ if ((f_copy = do_open(backupptr, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600)) < 0) {
+ rsyserr(FERROR_XFER, errno, "open %s", full_fname(backupptr));
+ unmake_file(back_file);
+ back_file = NULL;
+ close(fd);
+ goto cleanup;
+ }
+ fnamecmp_type = FNAMECMP_BACKUP;
+ }
+
+ if (DEBUG_GTE(DELTASUM, 3)) {
+ rprintf(FINFO, "gen mapped %s of size %s\n",
+ fnamecmp, big_num(sx.st.st_size));
+ }
+
+ if (DEBUG_GTE(DELTASUM, 2))
+ rprintf(FINFO, "generating and sending sums for %d\n", ndx);
+
+ notify_others:
+ if (remove_source_files && !delay_updates && !phase && !dry_run)
+ increment_active_files(ndx, itemizing, code);
+ if (inc_recurse && (!dry_run || write_batch < 0))
+ cur_flist->in_progress++;
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && F_IS_HLINKED(file))
+ file->flags |= FLAG_FILE_SENT;
+#endif
+ write_ndx(f_out, ndx);
+ if (itemizing) {
+ int iflags = ITEM_TRANSFER;
+ if (always_checksum > 0)
+ iflags |= ITEM_REPORT_CHANGE;
+ if (fnamecmp_type != FNAMECMP_FNAME)
+ iflags |= ITEM_BASIS_TYPE_FOLLOWS;
+ if (fnamecmp_type >= FNAMECMP_FUZZY)
+ iflags |= ITEM_XNAME_FOLLOWS;
+ itemize(fnamecmp, file, -1, real_ret, &real_sx, iflags, fnamecmp_type,
+ fuzzy_file ? fuzzy_file->basename : NULL);
+ free_stat_x(&real_sx);
+ }
+
+ if (!do_xfers) {
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && F_IS_HLINKED(file))
+ finish_hard_link(file, fname, ndx, &sx.st, itemizing, code, -1);
+#endif
+ goto cleanup;
+ }
+ if (read_batch)
+ goto cleanup;
+
+ if (statret != 0 || whole_file)
+ write_sum_head(f_out, NULL);
+ else if (sx.st.st_size <= 0) {
+ write_sum_head(f_out, NULL);
+ close(fd);
+ } else {
+ if (generate_and_send_sums(fd, sx.st.st_size, f_out, f_copy) < 0) {
+ rprintf(FWARNING,
+ "WARNING: file is too large for checksum sending: %s\n",
+ fnamecmp);
+ write_sum_head(f_out, NULL);
+ }
+ close(fd);
+ }
+
+ cleanup:
+ if (back_file) {
+ int save_preserve_xattrs = preserve_xattrs;
+ if (f_copy >= 0)
+ close(f_copy);
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs) {
+ copy_xattrs(fname, backupptr);
+ preserve_xattrs = 0;
+ }
+#endif
+ set_file_attrs(backupptr, back_file, NULL, NULL, 0);
+ preserve_xattrs = save_preserve_xattrs;
+ if (INFO_GTE(BACKUP, 1)) {
+ rprintf(FINFO, "backed up %s to %s\n",
+ fname, backupptr);
+ }
+ unmake_file(back_file);
+ }
+
+ free_stat_x(&sx);
+}
+
+/* If we are replacing an existing hard link, symlink, device, or special file,
+ * create a temp-name item and rename it into place. A symlimk specifies slnk,
+ * a hard link specifies hlnk, otherwise we create a device based on rdev.
+ * Specify 0 for the del_for_flag if there is not a file to replace. This
+ * returns 1 on success and 0 on failure. */
+int atomic_create(struct file_struct *file, char *fname, const char *slnk, const char *hlnk,
+ dev_t rdev, stat_x *sxp, int del_for_flag)
+{
+ char tmpname[MAXPATHLEN];
+ const char *create_name;
+ int skip_atomic, dir_in_the_way = del_for_flag && S_ISDIR(sxp->st.st_mode);
+
+ if (!del_for_flag || dir_in_the_way || tmpdir || !get_tmpname(tmpname, fname, True))
+ skip_atomic = 1;
+ else
+ skip_atomic = 0;
+
+ if (del_for_flag) {
+ if (make_backups > 0 && !dir_in_the_way) {
+ if (!make_backup(fname, skip_atomic))
+ return 0;
+ } else if (skip_atomic) {
+ int del_opts = delete_mode || force_delete ? DEL_RECURSE : 0;
+ if (delete_item(fname, sxp->st.st_mode, del_opts | del_for_flag) != 0)
+ return 0;
+ }
+ }
+
+ create_name = skip_atomic ? fname : tmpname;
+
+ if (slnk) {
+#ifdef SUPPORT_LINKS
+ if (do_symlink(slnk, create_name) < 0) {
+ rsyserr(FERROR_XFER, errno, "symlink %s -> \"%s\" failed",
+ full_fname(create_name), slnk);
+ return 0;
+ }
+#else
+ return 0;
+#endif
+ } else if (hlnk) {
+#ifdef SUPPORT_HARD_LINKS
+ if (!hard_link_one(file, create_name, hlnk, 0))
+ return 0;
+#else
+ return 0;
+#endif
+ } else {
+ if (do_mknod(create_name, file->mode, rdev) < 0) {
+ rsyserr(FERROR_XFER, errno, "mknod %s failed",
+ full_fname(create_name));
+ return 0;
+ }
+ }
+
+ if (!skip_atomic) {
+ if (do_rename(tmpname, fname) < 0) {
+ rsyserr(FERROR_XFER, errno, "rename %s -> \"%s\" failed",
+ full_fname(tmpname), full_fname(fname));
+ do_unlink(tmpname);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+#ifdef SUPPORT_HARD_LINKS
+static void handle_skipped_hlink(struct file_struct *file, int itemizing,
+ enum logcode code, int f_out)
+{
+ char fbuf[MAXPATHLEN];
+ int new_last_ndx;
+ struct file_list *save_flist = cur_flist;
+
+ /* If we skip the last item in a chain of links and there was a
+ * prior non-skipped hard-link waiting to finish, finish it now. */
+ if ((new_last_ndx = skip_hard_link(file, &cur_flist)) < 0)
+ return;
+
+ file = cur_flist->files[new_last_ndx - cur_flist->ndx_start];
+ cur_flist->in_progress--; /* undo prior increment */
+ f_name(file, fbuf);
+ recv_generator(fbuf, file, new_last_ndx, itemizing, code, f_out);
+
+ cur_flist = save_flist;
+}
+#endif
+
+static void touch_up_dirs(struct file_list *flist, int ndx)
+{
+ static int counter = 0;
+ struct file_struct *file;
+ char *fname;
+ BOOL fix_dir_perms;
+ int i, start, end;
+
+ if (ndx < 0) {
+ start = 0;
+ end = flist->used - 1;
+ } else
+ start = end = ndx;
+
+ /* Fix any directory permissions that were modified during the
+ * transfer and/or re-set any tweaked modified-time values. */
+ for (i = start; i <= end; i++, counter++) {
+ file = flist->files[i];
+ if (!F_IS_ACTIVE(file))
+ continue;
+ if (!S_ISDIR(file->mode)
+ || (!implied_dirs && file->flags & FLAG_IMPLIED_DIR))
+ continue;
+ if (DEBUG_GTE(TIME, 2)) {
+ fname = f_name(file, NULL);
+ rprintf(FINFO, "touch_up_dirs: %s (%d)\n",
+ NS(fname), i);
+ }
+ /* Be sure not to retouch permissions with --fake-super. */
+ fix_dir_perms = !am_root && !(file->mode & S_IWUSR);
+ if (file->flags & FLAG_MISSING_DIR || !(need_retouch_dir_times || fix_dir_perms))
+ continue;
+ fname = f_name(file, NULL);
+ if (fix_dir_perms)
+ do_chmod(fname, file->mode);
+ if (need_retouch_dir_times) {
+ STRUCT_STAT st;
+ if (link_stat(fname, &st, 0) == 0
+ && cmp_time(st.st_mtime, file->modtime) != 0)
+ set_modtime(fname, file->modtime, F_MOD_NSEC(file), file->mode);
+ }
+ if (counter >= loopchk_limit) {
+ if (allowed_lull)
+ maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH);
+ else
+ maybe_flush_socket(0);
+ counter = 0;
+ }
+ }
+}
+
+void check_for_finished_files(int itemizing, enum logcode code, int check_redo)
+{
+ struct file_struct *file;
+ struct file_list *flist;
+ char fbuf[MAXPATHLEN];
+ int ndx;
+
+ while (1) {
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && (ndx = get_hlink_num()) != -1) {
+ int send_failed = (ndx == -2);
+ if (send_failed)
+ ndx = get_hlink_num();
+ flist = flist_for_ndx(ndx, "check_for_finished_files.1");
+ file = flist->files[ndx - flist->ndx_start];
+ assert(file->flags & FLAG_HLINKED);
+ if (send_failed)
+ handle_skipped_hlink(file, itemizing, code, sock_f_out);
+ else
+ finish_hard_link(file, f_name(file, fbuf), ndx, NULL, itemizing, code, -1);
+ flist->in_progress--;
+ continue;
+ }
+#endif
+
+ if (check_redo && (ndx = get_redo_num()) != -1) {
+ OFF_T save_max_size = max_size;
+ OFF_T save_min_size = min_size;
+ csum_length = SUM_LENGTH;
+ max_size = -1;
+ min_size = -1;
+ ignore_existing = -ignore_existing;
+ ignore_non_existing = -ignore_non_existing;
+ update_only = -update_only;
+ always_checksum = -always_checksum;
+ size_only = -size_only;
+ append_mode = -append_mode;
+ make_backups = -make_backups; /* avoid dup backup w/inplace */
+ ignore_times++;
+
+ flist = cur_flist;
+ cur_flist = flist_for_ndx(ndx, "check_for_finished_files.2");
+
+ file = cur_flist->files[ndx - cur_flist->ndx_start];
+ if (solo_file)
+ strlcpy(fbuf, solo_file, sizeof fbuf);
+ else
+ f_name(file, fbuf);
+ recv_generator(fbuf, file, ndx, itemizing, code, sock_f_out);
+ cur_flist->to_redo--;
+
+ cur_flist = flist;
+
+ csum_length = SHORT_SUM_LENGTH;
+ max_size = save_max_size;
+ min_size = save_min_size;
+ ignore_existing = -ignore_existing;
+ ignore_non_existing = -ignore_non_existing;
+ update_only = -update_only;
+ always_checksum = -always_checksum;
+ size_only = -size_only;
+ append_mode = -append_mode;
+ make_backups = -make_backups;
+ ignore_times--;
+ continue;
+ }
+
+ if (cur_flist == first_flist)
+ break;
+
+ /* We only get here if inc_recurse is enabled. */
+ if (first_flist->in_progress || first_flist->to_redo)
+ break;
+
+ write_ndx(sock_f_out, NDX_DONE);
+ if (!read_batch && !flist_eof) {
+ int old_total = 0;
+ for (flist = first_flist; flist != cur_flist; flist = flist->next)
+ old_total += flist->used;
+ maybe_flush_socket(!flist_eof && file_total - old_total < MIN_FILECNT_LOOKAHEAD/2);
+ }
+
+ if (delete_during == 2 || !dir_tweaking) {
+ /* Skip directory touch-up. */
+ } else if (first_flist->parent_ndx >= 0)
+ touch_up_dirs(dir_flist, first_flist->parent_ndx);
+
+ flist_free(first_flist); /* updates first_flist */
+ }
+}
+
+void generate_files(int f_out, const char *local_name)
+{
+ int i, ndx, next_loopchk = 0;
+ char fbuf[MAXPATHLEN];
+ int itemizing;
+ enum logcode code;
+ int save_info_flist = info_levels[INFO_FLIST];
+ int save_info_progress = info_levels[INFO_PROGRESS];
+
+ if (protocol_version >= 29) {
+ itemizing = 1;
+ maybe_ATTRS_REPORT = stdout_format_has_i ? 0 : ATTRS_REPORT;
+ code = logfile_format_has_i ? FNONE : FLOG;
+ } else if (am_daemon) {
+ itemizing = logfile_format_has_i && do_xfers;
+ maybe_ATTRS_REPORT = ATTRS_REPORT;
+ code = itemizing || !do_xfers ? FCLIENT : FINFO;
+ } else if (!am_server) {
+ itemizing = stdout_format_has_i;
+ maybe_ATTRS_REPORT = stdout_format_has_i ? 0 : ATTRS_REPORT;
+ code = itemizing ? FNONE : FINFO;
+ } else {
+ itemizing = 0;
+ maybe_ATTRS_REPORT = ATTRS_REPORT;
+ code = FINFO;
+ }
+ solo_file = local_name;
+ dir_tweaking = !(list_only || solo_file || dry_run);
+ need_retouch_dir_times = preserve_times & PRESERVE_DIR_TIMES;
+ loopchk_limit = allowed_lull ? allowed_lull * 5 : 200;
+ symlink_timeset_failed_flags = ITEM_REPORT_TIME
+ | (protocol_version >= 30 || !am_server ? ITEM_REPORT_TIMEFAIL : 0);
+ implied_dirs_are_missing = relative_paths && !implied_dirs && protocol_version < 30;
+
+ if (DEBUG_GTE(GENR, 1))
+ rprintf(FINFO, "generator starting pid=%d\n", (int)getpid());
+
+ if (delete_before && !solo_file && cur_flist->used > 0)
+ do_delete_pass();
+ if (delete_during == 2) {
+ deldelay_size = BIGPATHBUFLEN * 4;
+ deldelay_buf = new_array(char, deldelay_size);
+ if (!deldelay_buf)
+ out_of_memory("delete-delay");
+ }
+ info_levels[INFO_FLIST] = info_levels[INFO_PROGRESS] = 0;
+
+ if (append_mode > 0 || whole_file < 0)
+ whole_file = 0;
+ if (DEBUG_GTE(FLIST, 1)) {
+ rprintf(FINFO, "delta-transmission %s\n",
+ whole_file
+ ? "disabled for local transfer or --whole-file"
+ : "enabled");
+ }
+
+ dflt_perms = (ACCESSPERMS & ~orig_umask);
+
+ do {
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && inc_recurse) {
+ while (!flist_eof && file_total < MIN_FILECNT_LOOKAHEAD/2)
+ wait_for_receiver();
+ }
+#endif
+
+ if (inc_recurse && cur_flist->parent_ndx >= 0) {
+ struct file_struct *fp = dir_flist->files[cur_flist->parent_ndx];
+ if (solo_file)
+ strlcpy(fbuf, solo_file, sizeof fbuf);
+ else
+ f_name(fp, fbuf);
+ ndx = cur_flist->ndx_start - 1;
+ recv_generator(fbuf, fp, ndx, itemizing, code, f_out);
+ if (delete_during && dry_run < 2 && !list_only
+ && !(fp->flags & FLAG_MISSING_DIR)) {
+ if (fp->flags & FLAG_CONTENT_DIR) {
+ dev_t dirdev;
+ if (one_file_system) {
+ uint32 *devp = F_DIR_DEV_P(fp);
+ dirdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
+ } else
+ dirdev = MAKEDEV(0, 0);
+ delete_in_dir(fbuf, fp, &dirdev);
+ } else
+ change_local_filter_dir(fbuf, strlen(fbuf), F_DEPTH(fp));
+ }
+ }
+ for (i = cur_flist->low; i <= cur_flist->high; i++) {
+ struct file_struct *file = cur_flist->sorted[i];
+
+ if (!F_IS_ACTIVE(file))
+ continue;
+
+ if (unsort_ndx)
+ ndx = F_NDX(file);
+ else
+ ndx = i + cur_flist->ndx_start;
+
+ if (solo_file)
+ strlcpy(fbuf, solo_file, sizeof fbuf);
+ else
+ f_name(file, fbuf);
+ recv_generator(fbuf, file, ndx, itemizing, code, f_out);
+
+ check_for_finished_files(itemizing, code, 0);
+
+ if (i + cur_flist->ndx_start >= next_loopchk) {
+ if (allowed_lull)
+ maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH);
+ else
+ maybe_flush_socket(0);
+ next_loopchk += loopchk_limit;
+ }
+ }
+
+ if (!inc_recurse) {
+ write_ndx(f_out, NDX_DONE);
+ break;
+ }
+
+ while (1) {
+ check_for_finished_files(itemizing, code, 1);
+ if (cur_flist->next || flist_eof)
+ break;
+ wait_for_receiver();
+ }
+ } while ((cur_flist = cur_flist->next) != NULL);
+
+ if (delete_during)
+ delete_in_dir(NULL, NULL, &dev_zero);
+ phase++;
+ if (DEBUG_GTE(GENR, 1))
+ rprintf(FINFO, "generate_files phase=%d\n", phase);
+
+ while (1) {
+ check_for_finished_files(itemizing, code, 1);
+ if (msgdone_cnt)
+ break;
+ wait_for_receiver();
+ }
+
+ phase++;
+ if (DEBUG_GTE(GENR, 1))
+ rprintf(FINFO, "generate_files phase=%d\n", phase);
+
+ write_ndx(f_out, NDX_DONE);
+
+ /* Reduce round-trip lag-time for a useless delay-updates phase. */
+ if (protocol_version >= 29 && EARLY_DELAY_DONE_MSG())
+ write_ndx(f_out, NDX_DONE);
+
+ if (protocol_version >= 31 && EARLY_DELETE_DONE_MSG()) {
+ if ((INFO_GTE(STATS, 2) && (delete_mode || force_delete)) || read_batch)
+ write_del_stats(f_out);
+ if (EARLY_DELAY_DONE_MSG()) /* Can't send this before delay */
+ write_ndx(f_out, NDX_DONE);
+ }
+
+ /* Read MSG_DONE for the redo phase (and any prior messages). */
+ while (1) {
+ check_for_finished_files(itemizing, code, 0);
+ if (msgdone_cnt > 1)
+ break;
+ wait_for_receiver();
+ }
+
+ if (protocol_version >= 29) {
+ phase++;
+ if (DEBUG_GTE(GENR, 1))
+ rprintf(FINFO, "generate_files phase=%d\n", phase);
+ if (!EARLY_DELAY_DONE_MSG()) {
+ write_ndx(f_out, NDX_DONE);
+ if (protocol_version >= 31 && EARLY_DELETE_DONE_MSG())
+ write_ndx(f_out, NDX_DONE);
+ }
+ /* Read MSG_DONE for delay-updates phase & prior messages. */
+ while (msgdone_cnt == 2)
+ wait_for_receiver();
+ }
+
+ info_levels[INFO_FLIST] = save_info_flist;
+ info_levels[INFO_PROGRESS] = save_info_progress;
+
+ if (delete_during == 2)
+ do_delayed_deletions(fbuf);
+ if (delete_after && !solo_file && file_total > 0)
+ do_delete_pass();
+
+ if (max_delete >= 0 && skipped_deletes) {
+ rprintf(FWARNING,
+ "Deletions stopped due to --max-delete limit (%d skipped)\n",
+ skipped_deletes);
+ io_error |= IOERR_DEL_LIMIT;
+ }
+
+ if (protocol_version >= 31) {
+ if (!EARLY_DELETE_DONE_MSG()) {
+ if (INFO_GTE(STATS, 2) || read_batch)
+ write_del_stats(f_out);
+ write_ndx(f_out, NDX_DONE);
+ }
+
+ /* Read MSG_DONE for late-delete phase & prior messages. */
+ while (msgdone_cnt == 3)
+ wait_for_receiver();
+ }
+
+ if ((need_retouch_dir_perms || need_retouch_dir_times)
+ && dir_tweaking && (!inc_recurse || delete_during == 2))
+ touch_up_dirs(dir_flist, -1);
+
+ if (DEBUG_GTE(GENR, 1))
+ rprintf(FINFO, "generate_files finished\n");
+}
diff --git a/rsync/getfsdev.c b/rsync/getfsdev.c
new file mode 100644
index 0000000..3b113bd
--- /dev/null
+++ b/rsync/getfsdev.c
@@ -0,0 +1,23 @@
+#include "rsync.h"
+
+ int main(int argc, char *argv[])
+{
+ STRUCT_STAT st;
+ int ret;
+
+ while (--argc > 0) {
+#ifdef USE_STAT64_FUNCS
+ ret = stat64(*++argv, &st);
+#else
+ ret = stat(*++argv, &st);
+#endif
+ if (ret < 0) {
+ fprintf(stderr, "Unable to stat `%s'\n", *argv);
+ exit(1);
+ }
+ printf("%ld/%ld\n", (long)major(st.st_dev),
+ (long)minor(st.st_dev));
+ }
+
+ return 0;
+}
diff --git a/rsync/getgroups.c b/rsync/getgroups.c
new file mode 100644
index 0000000..bacb21b
--- /dev/null
+++ b/rsync/getgroups.c
@@ -0,0 +1,62 @@
+/*
+ * Print out the gids of all groups for the current user. This is like
+ * `id -G` on Linux, but it's too hard to find a portable equivalent.
+ *
+ * Copyright (C) 2002 Martin Pool
+ * Copyright (C) 2003-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+int
+main(UNUSED(int argc), UNUSED(char *argv[]))
+{
+ int n, i;
+ gid_t *list;
+ gid_t gid = MY_GID();
+ int gid_in_list = 0;
+
+#ifdef HAVE_GETGROUPS
+ if ((n = getgroups(0, NULL)) < 0) {
+ perror("getgroups");
+ return 1;
+ }
+#else
+ n = 0;
+#endif
+
+ list = (gid_t*)malloc(sizeof (gid_t) * (n + 1));
+ if (!list) {
+ fprintf(stderr, "out of memory!\n");
+ exit(1);
+ }
+
+#ifdef HAVE_GETGROUPS
+ if (n > 0)
+ n = getgroups(n, list);
+#endif
+
+ for (i = 0; i < n; i++) {
+ printf("%lu ", (unsigned long)list[i]);
+ if (list[i] == gid)
+ gid_in_list = 1;
+ }
+ /* The default gid might not be in the list on some systems. */
+ if (!gid_in_list)
+ printf("%lu", (unsigned long)gid);
+ printf("\n");
+
+ return 0;
+}
diff --git a/rsync/hashtable.c b/rsync/hashtable.c
new file mode 100644
index 0000000..5cdcf61
--- /dev/null
+++ b/rsync/hashtable.c
@@ -0,0 +1,172 @@
+/*
+ * Routines to provide a memory-efficient hashtable.
+ *
+ * Copyright (C) 2007-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+#define HASH_LOAD_LIMIT(size) ((size)*3/4)
+
+struct hashtable *hashtable_create(int size, int key64)
+{
+ int req = size;
+ struct hashtable *tbl;
+ int node_size = key64 ? sizeof (struct ht_int64_node)
+ : sizeof (struct ht_int32_node);
+
+ /* Pick a power of 2 that can hold the requested size. */
+ if (size & (size-1) || size < 16) {
+ size = 16;
+ while (size < req)
+ size *= 2;
+ }
+
+ if (!(tbl = new(struct hashtable))
+ || !(tbl->nodes = new_array0(char, size * node_size)))
+ out_of_memory("hashtable_create");
+ tbl->size = size;
+ tbl->entries = 0;
+ tbl->node_size = node_size;
+ tbl->key64 = key64 ? 1 : 0;
+
+ if (DEBUG_GTE(HASH, 1)) {
+ char buf[32];
+ if (req != size)
+ snprintf(buf, sizeof buf, "req: %d, ", req);
+ else
+ *buf = '\0';
+ rprintf(FINFO, "[%s] created hashtable %lx (%ssize: %d, keys: %d-bit)\n",
+ who_am_i(), (long)tbl, buf, size, key64 ? 64 : 32);
+ }
+
+ return tbl;
+}
+
+void hashtable_destroy(struct hashtable *tbl)
+{
+ if (DEBUG_GTE(HASH, 1)) {
+ rprintf(FINFO, "[%s] destroyed hashtable %lx (size: %d, keys: %d-bit)\n",
+ who_am_i(), (long)tbl, tbl->size, tbl->key64 ? 64 : 32);
+ }
+ free(tbl->nodes);
+ free(tbl);
+}
+
+/* This returns the node for the indicated key, either newly created or
+ * already existing. Returns NULL if not allocating and not found. */
+void *hashtable_find(struct hashtable *tbl, int64 key, int allocate_if_missing)
+{
+ int key64 = tbl->key64;
+ struct ht_int32_node *node;
+ uint32 ndx;
+
+ if (key64 ? key == 0 : (int32)key == 0) {
+ rprintf(FERROR, "Internal hashtable error: illegal key supplied!\n");
+ exit_cleanup(RERR_MESSAGEIO);
+ }
+
+ if (allocate_if_missing && tbl->entries > HASH_LOAD_LIMIT(tbl->size)) {
+ void *old_nodes = tbl->nodes;
+ int size = tbl->size * 2;
+ int i;
+
+ if (!(tbl->nodes = new_array0(char, size * tbl->node_size)))
+ out_of_memory("hashtable_node");
+ tbl->size = size;
+ tbl->entries = 0;
+
+ if (DEBUG_GTE(HASH, 1)) {
+ rprintf(FINFO, "[%s] growing hashtable %lx (size: %d, keys: %d-bit)\n",
+ who_am_i(), (long)tbl, size, key64 ? 64 : 32);
+ }
+
+ for (i = size / 2; i-- > 0; ) {
+ struct ht_int32_node *move_node = HT_NODE(tbl, old_nodes, i);
+ int64 move_key = HT_KEY(move_node, key64);
+ if (move_key == 0)
+ continue;
+ node = hashtable_find(tbl, move_key, 1);
+ node->data = move_node->data;
+ }
+
+ free(old_nodes);
+ }
+
+ if (!key64) {
+ /* Based on Jenkins One-at-a-time hash. */
+ uchar buf[4], *keyp = buf;
+ int i;
+
+ SIVALu(buf, 0, key);
+ for (ndx = 0, i = 0; i < 4; i++) {
+ ndx += keyp[i];
+ ndx += (ndx << 10);
+ ndx ^= (ndx >> 6);
+ }
+ ndx += (ndx << 3);
+ ndx ^= (ndx >> 11);
+ ndx += (ndx << 15);
+ } else {
+ /* Based on Jenkins hashword() from lookup3.c. */
+ uint32 a, b, c;
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + (8 << 2);
+
+#define rot(x,k) (((x)<<(k)) ^ ((x)>>(32-(k))))
+#if SIZEOF_INT64 >= 8
+ b += (uint32)(key >> 32);
+#endif
+ a += (uint32)key;
+ c ^= b; c -= rot(b, 14);
+ a ^= c; a -= rot(c, 11);
+ b ^= a; b -= rot(a, 25);
+ c ^= b; c -= rot(b, 16);
+ a ^= c; a -= rot(c, 4);
+ b ^= a; b -= rot(a, 14);
+ c ^= b; c -= rot(b, 24);
+#undef rot
+ ndx = c;
+ }
+
+ /* If it already exists, return the node. If we're not
+ * allocating, return NULL if the key is not found. */
+ while (1) {
+ int64 nkey;
+
+ ndx &= tbl->size - 1;
+ node = HT_NODE(tbl, tbl->nodes, ndx);
+ nkey = HT_KEY(node, key64);
+
+ if (nkey == key)
+ return node;
+ if (nkey == 0) {
+ if (!allocate_if_missing)
+ return NULL;
+ break;
+ }
+ ndx++;
+ }
+
+ /* Take over this empty spot and then return the node. */
+ if (key64)
+ ((struct ht_int64_node*)node)->key = key;
+ else
+ node->key = (int32)key;
+ tbl->entries++;
+ return node;
+}
diff --git a/rsync/hlink.c b/rsync/hlink.c
new file mode 100644
index 0000000..3b57898
--- /dev/null
+++ b/rsync/hlink.c
@@ -0,0 +1,571 @@
+/*
+ * Routines to support hard-linking.
+ *
+ * Copyright (C) 1996 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2002 Martin Pool
+ * Copyright (C) 2004-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "inums.h"
+#include "ifuncs.h"
+
+extern int dry_run;
+extern int list_only;
+extern int am_sender;
+extern int inc_recurse;
+extern int do_xfers;
+extern int link_dest;
+extern int preserve_acls;
+extern int preserve_xattrs;
+extern int protocol_version;
+extern int remove_source_files;
+extern int stdout_format_has_i;
+extern int maybe_ATTRS_REPORT;
+extern int unsort_ndx;
+extern char *basis_dir[MAX_BASIS_DIRS+1];
+extern struct file_list *cur_flist;
+
+#ifdef SUPPORT_HARD_LINKS
+
+/* Starting with protocol 30, we use a simple hashtable on the sending side
+ * for hashing the st_dev and st_ino info. The receiving side gets told
+ * (via flags and a "group index") which items are hard-linked together, so
+ * we can avoid the pool of dev+inode data. For incremental recursion mode,
+ * the receiver will use a ndx hash to remember old pathnames. */
+
+static struct hashtable *dev_tbl;
+
+static struct hashtable *prior_hlinks;
+
+static struct file_list *hlink_flist;
+
+void init_hard_links(void)
+{
+ if (am_sender || protocol_version < 30)
+ dev_tbl = hashtable_create(16, 1);
+ else if (inc_recurse)
+ prior_hlinks = hashtable_create(1024, 0);
+}
+
+struct ht_int64_node *idev_find(int64 dev, int64 ino)
+{
+ static struct ht_int64_node *dev_node = NULL;
+ struct hashtable *tbl;
+
+ /* Note that some OSes have a dev == 0, so increment to avoid storing a 0. */
+ if (!dev_node || dev_node->key != dev+1) {
+ /* We keep a separate hash table of inodes for every device. */
+ dev_node = hashtable_find(dev_tbl, dev+1, 1);
+ if (!(tbl = dev_node->data)) {
+ tbl = dev_node->data = hashtable_create(512, 1);
+ if (DEBUG_GTE(HLINK, 3)) {
+ rprintf(FINFO,
+ "[%s] created hashtable for dev %s\n",
+ who_am_i(), big_num(dev));
+ }
+ }
+ } else
+ tbl = dev_node->data;
+
+ return hashtable_find(tbl, ino, 1);
+}
+
+void idev_destroy(void)
+{
+ int i;
+
+ for (i = 0; i < dev_tbl->size; i++) {
+ struct ht_int32_node *node = HT_NODE(dev_tbl, dev_tbl->nodes, i);
+ if (node->data)
+ hashtable_destroy(node->data);
+ }
+
+ hashtable_destroy(dev_tbl);
+}
+
+static int hlink_compare_gnum(int *int1, int *int2)
+{
+ struct file_struct *f1 = hlink_flist->sorted[*int1];
+ struct file_struct *f2 = hlink_flist->sorted[*int2];
+ int32 gnum1 = F_HL_GNUM(f1);
+ int32 gnum2 = F_HL_GNUM(f2);
+
+ if (gnum1 != gnum2)
+ return gnum1 > gnum2 ? 1 : -1;
+
+ return *int1 > *int2 ? 1 : -1;
+}
+
+static void match_gnums(int32 *ndx_list, int ndx_count)
+{
+ int32 from, prev;
+ struct file_struct *file, *file_next;
+ struct ht_int32_node *node = NULL;
+ int32 gnum, gnum_next;
+
+ qsort(ndx_list, ndx_count, sizeof ndx_list[0],
+ (int (*)()) hlink_compare_gnum);
+
+ for (from = 0; from < ndx_count; from++) {
+ file = hlink_flist->sorted[ndx_list[from]];
+ gnum = F_HL_GNUM(file);
+ if (inc_recurse) {
+ node = hashtable_find(prior_hlinks, gnum, 1);
+ if (!node->data) {
+ if (!(node->data = new_array0(char, 5)))
+ out_of_memory("match_gnums");
+ assert(gnum >= hlink_flist->ndx_start);
+ file->flags |= FLAG_HLINK_FIRST;
+ prev = -1;
+ } else if (CVAL(node->data, 0) == 0) {
+ struct file_list *flist;
+ prev = IVAL(node->data, 1);
+ flist = flist_for_ndx(prev, NULL);
+ if (flist)
+ flist->files[prev - flist->ndx_start]->flags &= ~FLAG_HLINK_LAST;
+ else {
+ /* We skipped all prior files in this
+ * group, so mark this as a "first". */
+ file->flags |= FLAG_HLINK_FIRST;
+ prev = -1;
+ }
+ } else
+ prev = -1;
+ } else {
+ file->flags |= FLAG_HLINK_FIRST;
+ prev = -1;
+ }
+ for ( ; from < ndx_count-1; file = file_next, gnum = gnum_next, from++) { /*SHARED ITERATOR*/
+ file_next = hlink_flist->sorted[ndx_list[from+1]];
+ gnum_next = F_HL_GNUM(file_next);
+ if (gnum != gnum_next)
+ break;
+ F_HL_PREV(file) = prev;
+ /* The linked list uses over-the-wire ndx values. */
+ if (unsort_ndx)
+ prev = F_NDX(file);
+ else
+ prev = ndx_list[from] + hlink_flist->ndx_start;
+ }
+ if (prev < 0 && !inc_recurse) {
+ /* Disable hard-link bit and set DONE so that
+ * HLINK_BUMP()-dependent values are unaffected. */
+ file->flags &= ~(FLAG_HLINKED | FLAG_HLINK_FIRST);
+ file->flags |= FLAG_HLINK_DONE;
+ continue;
+ }
+
+ file->flags |= FLAG_HLINK_LAST;
+ F_HL_PREV(file) = prev;
+ if (inc_recurse && CVAL(node->data, 0) == 0) {
+ if (unsort_ndx)
+ prev = F_NDX(file);
+ else
+ prev = ndx_list[from] + hlink_flist->ndx_start;
+ SIVAL(node->data, 1, prev);
+ }
+ }
+}
+
+/* Analyze the hard-links in the file-list by creating a list of all the
+ * items that have hlink data, sorting them, and matching up identical
+ * values into clusters. These will be a single linked list from last
+ * to first when we're done. */
+void match_hard_links(struct file_list *flist)
+{
+ if (!list_only && flist->used) {
+ int i, ndx_count = 0;
+ int32 *ndx_list;
+
+ if (!(ndx_list = new_array(int32, flist->used)))
+ out_of_memory("match_hard_links");
+
+ for (i = 0; i < flist->used; i++) {
+ if (F_IS_HLINKED(flist->sorted[i]))
+ ndx_list[ndx_count++] = i;
+ }
+
+ hlink_flist = flist;
+
+ if (ndx_count)
+ match_gnums(ndx_list, ndx_count);
+
+ free(ndx_list);
+ }
+ if (protocol_version < 30)
+ idev_destroy();
+}
+
+static int maybe_hard_link(struct file_struct *file, int ndx,
+ char *fname, int statret, stat_x *sxp,
+ const char *oldname, STRUCT_STAT *old_stp,
+ const char *realname, int itemizing, enum logcode code)
+{
+ if (statret == 0) {
+ if (sxp->st.st_dev == old_stp->st_dev
+ && sxp->st.st_ino == old_stp->st_ino) {
+ if (itemizing) {
+ itemize(fname, file, ndx, statret, sxp,
+ ITEM_LOCAL_CHANGE | ITEM_XNAME_FOLLOWS,
+ 0, "");
+ }
+ if (INFO_GTE(NAME, 2) && maybe_ATTRS_REPORT)
+ rprintf(FCLIENT, "%s is uptodate\n", fname);
+ file->flags |= FLAG_HLINK_DONE;
+ return 0;
+ }
+ }
+
+ if (atomic_create(file, fname, NULL, oldname, MAKEDEV(0, 0), sxp, statret == 0 ? DEL_FOR_FILE : 0)) {
+ if (itemizing) {
+ itemize(fname, file, ndx, statret, sxp,
+ ITEM_LOCAL_CHANGE | ITEM_XNAME_FOLLOWS, 0,
+ realname);
+ }
+ if (code != FNONE && INFO_GTE(NAME, 1))
+ rprintf(code, "%s => %s\n", fname, realname);
+ return 0;
+ }
+
+ return -1;
+}
+
+/* Figure out if a prior entry is still there or if we just have a
+ * cached name for it. */
+static char *check_prior(struct file_struct *file, int gnum,
+ int *prev_ndx_p, struct file_list **flist_p)
+{
+ struct file_struct *fp;
+ struct ht_int32_node *node;
+ int prev_ndx = F_HL_PREV(file);
+
+ while (1) {
+ struct file_list *flist;
+ if (prev_ndx < 0
+ || (flist = flist_for_ndx(prev_ndx, NULL)) == NULL)
+ break;
+ fp = flist->files[prev_ndx - flist->ndx_start];
+ if (!(fp->flags & FLAG_SKIP_HLINK)) {
+ *prev_ndx_p = prev_ndx;
+ *flist_p = flist;
+ return NULL;
+ }
+ F_HL_PREV(file) = prev_ndx = F_HL_PREV(fp);
+ }
+
+ if (inc_recurse
+ && (node = hashtable_find(prior_hlinks, gnum, 0)) != NULL) {
+ assert(node->data != NULL);
+ if (CVAL(node->data, 0) != 0) {
+ *prev_ndx_p = -1;
+ *flist_p = NULL;
+ return node->data;
+ }
+ /* The prior file must have been skipped. */
+ F_HL_PREV(file) = -1;
+ }
+
+ *prev_ndx_p = -1;
+ *flist_p = NULL;
+ return NULL;
+}
+
+/* Only called if FLAG_HLINKED is set and FLAG_HLINK_FIRST is not. Returns:
+ * 0 = process the file, 1 = skip the file, -1 = error occurred. */
+int hard_link_check(struct file_struct *file, int ndx, char *fname,
+ int statret, stat_x *sxp, int itemizing,
+ enum logcode code)
+{
+ STRUCT_STAT prev_st;
+ char namebuf[MAXPATHLEN], altbuf[MAXPATHLEN];
+ char *realname, *prev_name;
+ struct file_list *flist;
+ int gnum = inc_recurse ? F_HL_GNUM(file) : -1;
+ int prev_ndx;
+
+ prev_name = realname = check_prior(file, gnum, &prev_ndx, &flist);
+
+ if (!prev_name) {
+ struct file_struct *prev_file;
+
+ if (!flist) {
+ /* The previous file was skipped, so this one is
+ * treated as if it were the first in its group. */
+ if (DEBUG_GTE(HLINK, 2)) {
+ rprintf(FINFO, "hlink for %d (%s,%d): virtual first\n",
+ ndx, f_name(file, NULL), gnum);
+ }
+ return 0;
+ }
+
+ prev_file = flist->files[prev_ndx - flist->ndx_start];
+
+ /* Is the previous link not complete yet? */
+ if (!(prev_file->flags & FLAG_HLINK_DONE)) {
+ /* Is the previous link being transferred? */
+ if (prev_file->flags & FLAG_FILE_SENT) {
+ /* Add ourselves to the list of files that will
+ * be updated when the transfer completes, and
+ * mark ourself as waiting for the transfer. */
+ F_HL_PREV(file) = F_HL_PREV(prev_file);
+ F_HL_PREV(prev_file) = ndx;
+ file->flags |= FLAG_FILE_SENT;
+ cur_flist->in_progress++;
+ if (DEBUG_GTE(HLINK, 2)) {
+ rprintf(FINFO, "hlink for %d (%s,%d): waiting for %d\n",
+ ndx, f_name(file, NULL), gnum, F_HL_PREV(file));
+ }
+ return 1;
+ }
+ if (DEBUG_GTE(HLINK, 2)) {
+ rprintf(FINFO, "hlink for %d (%s,%d): looking for a leader\n",
+ ndx, f_name(file, NULL), gnum);
+ }
+ return 0;
+ }
+
+ /* There is a finished file to link with! */
+ if (!(prev_file->flags & FLAG_HLINK_FIRST)) {
+ /* The previous previous is FIRST when prev is not. */
+ prev_name = realname = check_prior(prev_file, gnum, &prev_ndx, &flist);
+ /* Update our previous pointer to point to the FIRST. */
+ F_HL_PREV(file) = prev_ndx;
+ }
+
+ if (!prev_name) {
+ int alt_dest;
+
+ assert(flist != NULL);
+ prev_file = flist->files[prev_ndx - flist->ndx_start];
+ /* F_HL_PREV() is alt_dest value when DONE && FIRST. */
+ alt_dest = F_HL_PREV(prev_file);
+ if (DEBUG_GTE(HLINK, 2)) {
+ rprintf(FINFO, "hlink for %d (%s,%d): found flist match (alt %d)\n",
+ ndx, f_name(file, NULL), gnum, alt_dest);
+ }
+
+ if (alt_dest >= 0 && dry_run) {
+ pathjoin(namebuf, MAXPATHLEN, basis_dir[alt_dest],
+ f_name(prev_file, NULL));
+ prev_name = namebuf;
+ realname = f_name(prev_file, altbuf);
+ } else {
+ prev_name = f_name(prev_file, namebuf);
+ realname = prev_name;
+ }
+ }
+ }
+
+ if (DEBUG_GTE(HLINK, 2)) {
+ rprintf(FINFO, "hlink for %d (%s,%d): leader is %d (%s)\n",
+ ndx, f_name(file, NULL), gnum, prev_ndx, prev_name);
+ }
+
+ if (link_stat(prev_name, &prev_st, 0) < 0) {
+ if (!dry_run || errno != ENOENT) {
+ rsyserr(FERROR_XFER, errno, "stat %s failed", full_fname(prev_name));
+ return -1;
+ }
+ /* A new hard-link will get a new dev & inode, so approximate
+ * those values in dry-run mode by zeroing them. */
+ memset(&prev_st, 0, sizeof prev_st);
+ }
+
+ if (statret < 0 && basis_dir[0] != NULL) {
+ /* If we match an alt-dest item, we don't output this as a change. */
+ char cmpbuf[MAXPATHLEN];
+ stat_x alt_sx;
+ int j = 0;
+ init_stat_x(&alt_sx);
+ do {
+ pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname);
+ if (link_stat(cmpbuf, &alt_sx.st, 0) < 0)
+ continue;
+ if (link_dest) {
+ if (prev_st.st_dev != alt_sx.st.st_dev
+ || prev_st.st_ino != alt_sx.st.st_ino)
+ continue;
+ statret = 1;
+ if (stdout_format_has_i == 0
+ || (!INFO_GTE(NAME, 2) && stdout_format_has_i < 2)) {
+ itemizing = 0;
+ code = FNONE;
+ if (INFO_GTE(NAME, 2) && maybe_ATTRS_REPORT)
+ rprintf(FCLIENT, "%s is uptodate\n", fname);
+ }
+ break;
+ }
+ if (!unchanged_file(cmpbuf, file, &alt_sx.st))
+ continue;
+ statret = 1;
+ if (unchanged_attrs(cmpbuf, file, &alt_sx))
+ break;
+ } while (basis_dir[++j] != NULL);
+ if (statret == 1) {
+ sxp->st = alt_sx.st;
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && !S_ISLNK(file->mode)) {
+ free_acl(sxp);
+ if (!ACL_READY(alt_sx))
+ get_acl(cmpbuf, sxp);
+ else {
+ sxp->acc_acl = alt_sx.acc_acl;
+ sxp->def_acl = alt_sx.def_acl;
+ alt_sx.acc_acl = alt_sx.def_acl = NULL;
+ }
+ }
+#endif
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs) {
+ free_xattr(sxp);
+ if (!XATTR_READY(alt_sx))
+ get_xattr(cmpbuf, sxp);
+ else {
+ sxp->xattr = alt_sx.xattr;
+ alt_sx.xattr = NULL;
+ }
+ }
+#endif
+ } else
+ free_stat_x(&alt_sx);
+ }
+
+ if (maybe_hard_link(file, ndx, fname, statret, sxp, prev_name, &prev_st,
+ realname, itemizing, code) < 0)
+ return -1;
+
+ if (remove_source_files == 1 && do_xfers)
+ send_msg_int(MSG_SUCCESS, ndx);
+
+ return 1;
+}
+
+int hard_link_one(struct file_struct *file, const char *fname,
+ const char *oldname, int terse)
+{
+ if (do_link(oldname, fname) < 0) {
+ enum logcode code;
+ if (terse) {
+ if (!INFO_GTE(NAME, 1))
+ return 0;
+ code = FINFO;
+ } else
+ code = FERROR_XFER;
+ rsyserr(code, errno, "link %s => %s failed",
+ full_fname(fname), oldname);
+ return 0;
+ }
+
+ file->flags |= FLAG_HLINK_DONE;
+
+ return 1;
+}
+
+void finish_hard_link(struct file_struct *file, const char *fname, int fin_ndx,
+ STRUCT_STAT *stp, int itemizing, enum logcode code,
+ int alt_dest)
+{
+ stat_x prev_sx;
+ STRUCT_STAT st;
+ char prev_name[MAXPATHLEN], alt_name[MAXPATHLEN];
+ const char *our_name;
+ struct file_list *flist;
+ int prev_statret, ndx, prev_ndx = F_HL_PREV(file);
+
+ if (stp == NULL && prev_ndx >= 0) {
+ if (link_stat(fname, &st, 0) < 0 && !dry_run) {
+ rsyserr(FERROR_XFER, errno, "stat %s failed",
+ full_fname(fname));
+ return;
+ }
+ stp = &st;
+ }
+
+ /* FIRST combined with DONE means we were the first to get done. */
+ file->flags |= FLAG_HLINK_FIRST | FLAG_HLINK_DONE;
+ F_HL_PREV(file) = alt_dest;
+ if (alt_dest >= 0 && dry_run) {
+ pathjoin(alt_name, MAXPATHLEN, basis_dir[alt_dest],
+ f_name(file, NULL));
+ our_name = alt_name;
+ } else
+ our_name = fname;
+
+ init_stat_x(&prev_sx);
+
+ while ((ndx = prev_ndx) >= 0) {
+ int val;
+ flist = flist_for_ndx(ndx, "finish_hard_link");
+ file = flist->files[ndx - flist->ndx_start];
+ file->flags = (file->flags & ~FLAG_HLINK_FIRST) | FLAG_HLINK_DONE;
+ prev_ndx = F_HL_PREV(file);
+ F_HL_PREV(file) = fin_ndx;
+ prev_statret = link_stat(f_name(file, prev_name), &prev_sx.st, 0);
+ val = maybe_hard_link(file, ndx, prev_name, prev_statret, &prev_sx,
+ our_name, stp, fname, itemizing, code);
+ flist->in_progress--;
+ free_stat_x(&prev_sx);
+ if (val < 0)
+ continue;
+ if (remove_source_files == 1 && do_xfers)
+ send_msg_int(MSG_SUCCESS, ndx);
+ }
+
+ if (inc_recurse) {
+ int gnum = F_HL_GNUM(file);
+ struct ht_int32_node *node = hashtable_find(prior_hlinks, gnum, 0);
+ if (node == NULL) {
+ rprintf(FERROR, "Unable to find a hlink node for %d (%s)\n", gnum, f_name(file, prev_name));
+ exit_cleanup(RERR_MESSAGEIO);
+ }
+ if (node->data == NULL) {
+ rprintf(FERROR, "Hlink node data for %d is NULL (%s)\n", gnum, f_name(file, prev_name));
+ exit_cleanup(RERR_MESSAGEIO);
+ }
+ if (CVAL(node->data, 0) != 0) {
+ rprintf(FERROR, "Hlink node data for %d already has path=%s (%s)\n",
+ gnum, (char*)node->data, f_name(file, prev_name));
+ exit_cleanup(RERR_MESSAGEIO);
+ }
+ free(node->data);
+ if (!(node->data = strdup(our_name)))
+ out_of_memory("finish_hard_link");
+ }
+}
+
+int skip_hard_link(struct file_struct *file, struct file_list **flist_p)
+{
+ struct file_list *flist;
+ int prev_ndx;
+
+ file->flags |= FLAG_SKIP_HLINK;
+ if (!(file->flags & FLAG_HLINK_LAST))
+ return -1;
+
+ check_prior(file, F_HL_GNUM(file), &prev_ndx, &flist);
+ if (prev_ndx >= 0) {
+ file = flist->files[prev_ndx - flist->ndx_start];
+ if (file->flags & (FLAG_HLINK_DONE|FLAG_FILE_SENT))
+ return -1;
+ file->flags |= FLAG_HLINK_LAST;
+ *flist_p = flist;
+ }
+
+ return prev_ndx;
+}
+#endif
diff --git a/rsync/ifuncs.h b/rsync/ifuncs.h
new file mode 100644
index 0000000..6b119aa
--- /dev/null
+++ b/rsync/ifuncs.h
@@ -0,0 +1,106 @@
+/* Inline functions for rsync.
+ *
+ * Copyright (C) 2007-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+static inline void
+alloc_xbuf(xbuf *xb, size_t sz)
+{
+ if (!(xb->buf = new_array(char, sz)))
+ out_of_memory("alloc_xbuf");
+ xb->size = sz;
+ xb->len = xb->pos = 0;
+}
+
+static inline void
+realloc_xbuf(xbuf *xb, size_t sz)
+{
+ char *bf = realloc_array(xb->buf, char, sz);
+ if (!bf)
+ out_of_memory("realloc_xbuf");
+ xb->buf = bf;
+ xb->size = sz;
+}
+
+static inline void
+free_xbuf(xbuf *xb)
+{
+ if (xb->buf)
+ free(xb->buf);
+ memset(xb, 0, sizeof (xbuf));
+}
+
+static inline int
+to_wire_mode(mode_t mode)
+{
+#ifdef SUPPORT_LINKS
+#if _S_IFLNK != 0120000
+ if (S_ISLNK(mode))
+ return (mode & ~(_S_IFMT)) | 0120000;
+#endif
+#endif
+ return mode;
+}
+
+static inline mode_t
+from_wire_mode(int mode)
+{
+#if _S_IFLNK != 0120000
+ if ((mode & (_S_IFMT)) == 0120000)
+ return (mode & ~(_S_IFMT)) | _S_IFLNK;
+#endif
+ return mode;
+}
+
+static inline char *
+d_name(struct dirent *di)
+{
+#ifdef HAVE_BROKEN_READDIR
+ return (di->d_name - 2);
+#else
+ return di->d_name;
+#endif
+}
+
+static inline void
+init_stat_x(stat_x *sx_p)
+{
+#ifdef SUPPORT_ACLS
+ sx_p->acc_acl = sx_p->def_acl = NULL;
+#endif
+#ifdef SUPPORT_XATTRS
+ sx_p->xattr = NULL;
+#endif
+}
+
+static inline void
+free_stat_x(stat_x *sx_p)
+{
+#ifdef SUPPORT_ACLS
+ {
+ extern int preserve_acls;
+ if (preserve_acls)
+ free_acl(sx_p);
+ }
+#endif
+#ifdef SUPPORT_XATTRS
+ {
+ extern int preserve_xattrs;
+ if (preserve_xattrs)
+ free_xattr(sx_p);
+ }
+#endif
+}
diff --git a/rsync/install-sh b/rsync/install-sh
new file mode 100755
index 0000000..956817d
--- /dev/null
+++ b/rsync/install-sh
@@ -0,0 +1,238 @@
+#! /bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+#
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/_inst.$$_
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
diff --git a/rsync/inums.h b/rsync/inums.h
new file mode 100644
index 0000000..3980b7a
--- /dev/null
+++ b/rsync/inums.h
@@ -0,0 +1,57 @@
+/* Inline functions for rsync.
+ *
+ * Copyright (C) 2008-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+static inline char *
+big_num(int64 num)
+{
+ return do_big_num(num, 0, NULL);
+}
+
+static inline char *
+comma_num(int64 num)
+{
+ extern int human_readable;
+ return do_big_num(num, human_readable != 0, NULL);
+}
+
+static inline char *
+human_num(int64 num)
+{
+ extern int human_readable;
+ return do_big_num(num, human_readable, NULL);
+}
+
+static inline char *
+big_dnum(double dnum, int decimal_digits)
+{
+ return do_big_dnum(dnum, 0, decimal_digits);
+}
+
+static inline char *
+comma_dnum(double dnum, int decimal_digits)
+{
+ extern int human_readable;
+ return do_big_dnum(dnum, human_readable != 0, decimal_digits);
+}
+
+static inline char *
+human_dnum(double dnum, int decimal_digits)
+{
+ extern int human_readable;
+ return do_big_dnum(dnum, human_readable, decimal_digits);
+}
diff --git a/rsync/io.c b/rsync/io.c
new file mode 100644
index 0000000..b9a9bd0
--- /dev/null
+++ b/rsync/io.c
@@ -0,0 +1,2384 @@
+/*
+ * Socket and pipe I/O utilities used in rsync.
+ *
+ * Copyright (C) 1996-2001 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2001, 2002 Martin Pool
+ * Copyright (C) 2003-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+/* Rsync provides its own multiplexing system, which is used to send
+ * stderr and stdout over a single socket.
+ *
+ * For historical reasons this is off during the start of the
+ * connection, but it's switched on quite early using
+ * io_start_multiplex_out() and io_start_multiplex_in(). */
+
+#include "rsync.h"
+#include "ifuncs.h"
+#include "inums.h"
+
+/** If no timeout is specified then use a 60 second select timeout */
+#define SELECT_TIMEOUT 60
+
+extern int bwlimit;
+extern size_t bwlimit_writemax;
+extern int io_timeout;
+extern int am_server;
+extern int am_sender;
+extern int am_receiver;
+extern int am_generator;
+extern int msgs2stderr;
+extern int inc_recurse;
+extern int io_error;
+extern int eol_nulls;
+extern int flist_eof;
+extern int file_total;
+extern int file_old_total;
+extern int list_only;
+extern int read_batch;
+extern int compat_flags;
+extern int protect_args;
+extern int checksum_seed;
+extern int protocol_version;
+extern int remove_source_files;
+extern int preserve_hard_links;
+extern BOOL extra_flist_sending_enabled;
+extern BOOL flush_ok_after_signal;
+extern struct stats stats;
+extern struct file_list *cur_flist;
+#ifdef ICONV_OPTION
+extern int filesfrom_convert;
+extern iconv_t ic_send, ic_recv;
+#endif
+
+int csum_length = SHORT_SUM_LENGTH; /* initial value */
+int allowed_lull = 0;
+int batch_fd = -1;
+int msgdone_cnt = 0;
+int forward_flist_data = 0;
+BOOL flist_receiving_enabled = False;
+
+/* Ignore an EOF error if non-zero. See whine_about_eof(). */
+int kluge_around_eof = 0;
+int got_kill_signal = -1; /* is set to 0 only after multiplexed I/O starts */
+
+int sock_f_in = -1;
+int sock_f_out = -1;
+
+int64 total_data_read = 0;
+int64 total_data_written = 0;
+
+static struct {
+ xbuf in, out, msg;
+ int in_fd;
+ int out_fd; /* Both "out" and "msg" go to this fd. */
+ int in_multiplexed;
+ unsigned out_empty_len;
+ size_t raw_data_header_pos; /* in the out xbuf */
+ size_t raw_flushing_ends_before; /* in the out xbuf */
+ size_t raw_input_ends_before; /* in the in xbuf */
+} iobuf = { .in_fd = -1, .out_fd = -1 };
+
+static time_t last_io_in;
+static time_t last_io_out;
+
+static int write_batch_monitor_in = -1;
+static int write_batch_monitor_out = -1;
+
+static int ff_forward_fd = -1;
+static int ff_reenable_multiplex = -1;
+static char ff_lastchar = '\0';
+static xbuf ff_xb = EMPTY_XBUF;
+#ifdef ICONV_OPTION
+static xbuf iconv_buf = EMPTY_XBUF;
+#endif
+static int select_timeout = SELECT_TIMEOUT;
+static int active_filecnt = 0;
+static OFF_T active_bytecnt = 0;
+static int first_message = 1;
+
+static char int_byte_extra[64] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* (00 - 3F)/4 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* (40 - 7F)/4 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* (80 - BF)/4 */
+ 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 5, 6, /* (C0 - FF)/4 */
+};
+
+/* Our I/O buffers are sized with no bits on in the lowest byte of the "size"
+ * (indeed, our rounding of sizes in 1024-byte units assures more than this).
+ * This allows the code that is storing bytes near the physical end of a
+ * circular buffer to temporarily reduce the buffer's size (in order to make
+ * some storing idioms easier), while also making it simple to restore the
+ * buffer's actual size when the buffer's "pos" wraps around to the start (we
+ * just round the buffer's size up again). */
+
+#define IOBUF_WAS_REDUCED(siz) ((siz) & 0xFF)
+#define IOBUF_RESTORE_SIZE(siz) (((siz) | 0xFF) + 1)
+
+#define IN_MULTIPLEXED (iobuf.in_multiplexed != 0)
+#define IN_MULTIPLEXED_AND_READY (iobuf.in_multiplexed > 0)
+#define OUT_MULTIPLEXED (iobuf.out_empty_len != 0)
+
+#define PIO_NEED_INPUT (1<<0) /* The *_NEED_* flags are mutually exclusive. */
+#define PIO_NEED_OUTROOM (1<<1)
+#define PIO_NEED_MSGROOM (1<<2)
+
+#define PIO_CONSUME_INPUT (1<<4) /* Must becombined with PIO_NEED_INPUT. */
+
+#define PIO_INPUT_AND_CONSUME (PIO_NEED_INPUT | PIO_CONSUME_INPUT)
+#define PIO_NEED_FLAGS (PIO_NEED_INPUT | PIO_NEED_OUTROOM | PIO_NEED_MSGROOM)
+
+#define REMOTE_OPTION_ERROR "rsync: on remote machine: -"
+#define REMOTE_OPTION_ERROR2 ": unknown option"
+
+#define FILESFROM_BUFLEN 2048
+
+enum festatus { FES_SUCCESS, FES_REDO, FES_NO_SEND };
+
+static flist_ndx_list redo_list, hlink_list;
+
+static void read_a_msg(void);
+static void drain_multiplex_messages(void);
+static void sleep_for_bwlimit(int bytes_written);
+
+static void check_timeout(BOOL allow_keepalive, int keepalive_flags)
+{
+ time_t t, chk;
+
+ /* On the receiving side, the generator is now the one that decides
+ * when a timeout has occurred. When it is sifting through a lot of
+ * files looking for work, it will be sending keep-alive messages to
+ * the sender, and even though the receiver won't be sending/receiving
+ * anything (not even keep-alive messages), the successful writes to
+ * the sender will keep things going. If the receiver is actively
+ * receiving data, it will ensure that the generator knows that it is
+ * not idle by sending the generator keep-alive messages (since the
+ * generator might be blocked trying to send checksums, it needs to
+ * know that the receiver is active). Thus, as long as one or the
+ * other is successfully doing work, the generator will not timeout. */
+ if (!io_timeout)
+ return;
+
+ t = time(NULL);
+
+ if (allow_keepalive) {
+ /* This may put data into iobuf.msg w/o flushing. */
+ maybe_send_keepalive(t, keepalive_flags);
+ }
+
+ if (!last_io_in)
+ last_io_in = t;
+
+ if (am_receiver)
+ return;
+
+ chk = MAX(last_io_out, last_io_in);
+ if (t - chk >= io_timeout) {
+ if (am_server)
+ msgs2stderr = 1;
+ rprintf(FERROR, "[%s] io timeout after %d seconds -- exiting\n",
+ who_am_i(), (int)(t-chk));
+ exit_cleanup(RERR_TIMEOUT);
+ }
+}
+
+/* It's almost always an error to get an EOF when we're trying to read from the
+ * network, because the protocol is (for the most part) self-terminating.
+ *
+ * There is one case for the receiver when it is at the end of the transfer
+ * (hanging around reading any keep-alive packets that might come its way): if
+ * the sender dies before the generator's kill-signal comes through, we can end
+ * up here needing to loop until the kill-signal arrives. In this situation,
+ * kluge_around_eof will be < 0.
+ *
+ * There is another case for older protocol versions (< 24) where the module
+ * listing was not terminated, so we must ignore an EOF error in that case and
+ * exit. In this situation, kluge_around_eof will be > 0. */
+static NORETURN void whine_about_eof(BOOL allow_kluge)
+{
+ if (kluge_around_eof && allow_kluge) {
+ int i;
+ if (kluge_around_eof > 0)
+ exit_cleanup(0);
+ /* If we're still here after 10 seconds, exit with an error. */
+ for (i = 10*1000/20; i--; )
+ msleep(20);
+ }
+
+ rprintf(FERROR, RSYNC_NAME ": connection unexpectedly closed "
+ "(%s bytes received so far) [%s]\n",
+ big_num(stats.total_read), who_am_i());
+
+ exit_cleanup(RERR_STREAMIO);
+}
+
+/* Do a safe read, handling any needed looping and error handling.
+ * Returns the count of the bytes read, which will only be different
+ * from "len" if we encountered an EOF. This routine is not used on
+ * the socket except very early in the transfer. */
+static size_t safe_read(int fd, char *buf, size_t len)
+{
+ size_t got = 0;
+
+ assert(fd != iobuf.in_fd);
+
+ while (1) {
+ struct timeval tv;
+ fd_set r_fds, e_fds;
+ int cnt;
+
+ FD_ZERO(&r_fds);
+ FD_SET(fd, &r_fds);
+ FD_ZERO(&e_fds);
+ FD_SET(fd, &e_fds);
+ tv.tv_sec = select_timeout;
+ tv.tv_usec = 0;
+
+ cnt = select(fd+1, &r_fds, NULL, &e_fds, &tv);
+ if (cnt <= 0) {
+ if (cnt < 0 && errno == EBADF) {
+ rsyserr(FERROR, errno, "safe_read select failed [%s]",
+ who_am_i());
+ exit_cleanup(RERR_FILEIO);
+ }
+ check_timeout(1, MSK_ALLOW_FLUSH);
+ continue;
+ }
+
+ /*if (FD_ISSET(fd, &e_fds))
+ rprintf(FINFO, "select exception on fd %d\n", fd); */
+
+ if (FD_ISSET(fd, &r_fds)) {
+ int n = read(fd, buf + got, len - got);
+ if (DEBUG_GTE(IO, 2))
+ rprintf(FINFO, "[%s] safe_read(%d)=%ld\n", who_am_i(), fd, (long)n);
+ if (n == 0)
+ break;
+ if (n < 0) {
+ if (errno == EINTR)
+ continue;
+ rsyserr(FERROR, errno, "safe_read failed to read %ld bytes [%s]",
+ (long)len, who_am_i());
+ exit_cleanup(RERR_STREAMIO);
+ }
+ if ((got += (size_t)n) == len)
+ break;
+ }
+ }
+
+ return got;
+}
+
+static const char *what_fd_is(int fd)
+{
+ static char buf[20];
+
+ if (fd == sock_f_out)
+ return "socket";
+ else if (fd == iobuf.out_fd)
+ return "message fd";
+ else if (fd == batch_fd)
+ return "batch file";
+ else {
+ snprintf(buf, sizeof buf, "fd %d", fd);
+ return buf;
+ }
+}
+
+/* Do a safe write, handling any needed looping and error handling.
+ * Returns only if everything was successfully written. This routine
+ * is not used on the socket except very early in the transfer. */
+static void safe_write(int fd, const char *buf, size_t len)
+{
+ int n;
+
+ assert(fd != iobuf.out_fd);
+
+ n = write(fd, buf, len);
+ if ((size_t)n == len)
+ return;
+ if (n < 0) {
+ if (errno != EINTR && errno != EWOULDBLOCK && errno != EAGAIN) {
+ write_failed:
+ rsyserr(FERROR, errno,
+ "safe_write failed to write %ld bytes to %s [%s]",
+ (long)len, what_fd_is(fd), who_am_i());
+ exit_cleanup(RERR_STREAMIO);
+ }
+ } else {
+ buf += n;
+ len -= n;
+ }
+
+ while (len) {
+ struct timeval tv;
+ fd_set w_fds;
+ int cnt;
+
+ FD_ZERO(&w_fds);
+ FD_SET(fd, &w_fds);
+ tv.tv_sec = select_timeout;
+ tv.tv_usec = 0;
+
+ cnt = select(fd + 1, NULL, &w_fds, NULL, &tv);
+ if (cnt <= 0) {
+ if (cnt < 0 && errno == EBADF) {
+ rsyserr(FERROR, errno, "safe_write select failed on %s [%s]",
+ what_fd_is(fd), who_am_i());
+ exit_cleanup(RERR_FILEIO);
+ }
+ if (io_timeout)
+ maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH);
+ continue;
+ }
+
+ if (FD_ISSET(fd, &w_fds)) {
+ n = write(fd, buf, len);
+ if (n < 0) {
+ if (errno == EINTR)
+ continue;
+ goto write_failed;
+ }
+ buf += n;
+ len -= n;
+ }
+ }
+}
+
+/* This is only called when files-from data is known to be available. We read
+ * a chunk of data and put it into the output buffer. */
+static void forward_filesfrom_data(void)
+{
+ int len;
+
+ len = read(ff_forward_fd, ff_xb.buf + ff_xb.len, ff_xb.size - ff_xb.len);
+ if (len <= 0) {
+ if (len == 0 || errno != EINTR) {
+ /* Send end-of-file marker */
+ ff_forward_fd = -1;
+ write_buf(iobuf.out_fd, "\0\0", ff_lastchar ? 2 : 1);
+ free_xbuf(&ff_xb);
+ if (ff_reenable_multiplex >= 0)
+ io_start_multiplex_out(ff_reenable_multiplex);
+ }
+ return;
+ }
+
+ if (DEBUG_GTE(IO, 2))
+ rprintf(FINFO, "[%s] files-from read=%ld\n", who_am_i(), (long)len);
+
+#ifdef ICONV_OPTION
+ len += ff_xb.len;
+#endif
+
+ if (!eol_nulls) {
+ char *s = ff_xb.buf + len;
+ /* Transform CR and/or LF into '\0' */
+ while (s-- > ff_xb.buf) {
+ if (*s == '\n' || *s == '\r')
+ *s = '\0';
+ }
+ }
+
+ if (ff_lastchar)
+ ff_xb.pos = 0;
+ else {
+ char *s = ff_xb.buf;
+ /* Last buf ended with a '\0', so don't let this buf start with one. */
+ while (len && *s == '\0')
+ s++, len--;
+ ff_xb.pos = s - ff_xb.buf;
+ }
+
+#ifdef ICONV_OPTION
+ if (filesfrom_convert && len) {
+ char *sob = ff_xb.buf + ff_xb.pos, *s = sob;
+ char *eob = sob + len;
+ int flags = ICB_INCLUDE_BAD | ICB_INCLUDE_INCOMPLETE | ICB_CIRCULAR_OUT;
+ if (ff_lastchar == '\0')
+ flags |= ICB_INIT;
+ /* Convert/send each null-terminated string separately, skipping empties. */
+ while (s != eob) {
+ if (*s++ == '\0') {
+ ff_xb.len = s - sob - 1;
+ if (iconvbufs(ic_send, &ff_xb, &iobuf.out, flags) < 0)
+ exit_cleanup(RERR_PROTOCOL); /* impossible? */
+ write_buf(iobuf.out_fd, s-1, 1); /* Send the '\0'. */
+ while (s != eob && *s == '\0')
+ s++;
+ sob = s;
+ ff_xb.pos = sob - ff_xb.buf;
+ flags |= ICB_INIT;
+ }
+ }
+
+ if ((ff_xb.len = s - sob) == 0)
+ ff_lastchar = '\0';
+ else {
+ /* Handle a partial string specially, saving any incomplete chars. */
+ flags &= ~ICB_INCLUDE_INCOMPLETE;
+ if (iconvbufs(ic_send, &ff_xb, &iobuf.out, flags) < 0) {
+ if (errno == E2BIG)
+ exit_cleanup(RERR_PROTOCOL); /* impossible? */
+ if (ff_xb.pos)
+ memmove(ff_xb.buf, ff_xb.buf + ff_xb.pos, ff_xb.len);
+ }
+ ff_lastchar = 'x'; /* Anything non-zero. */
+ }
+ } else
+#endif
+
+ if (len) {
+ char *f = ff_xb.buf + ff_xb.pos;
+ char *t = ff_xb.buf;
+ char *eob = f + len;
+ /* Eliminate any multi-'\0' runs. */
+ while (f != eob) {
+ if (!(*t++ = *f++)) {
+ while (f != eob && *f == '\0')
+ f++;
+ }
+ }
+ ff_lastchar = f[-1];
+ if ((len = t - ff_xb.buf) != 0) {
+ /* This will not circle back to perform_io() because we only get
+ * called when there is plenty of room in the output buffer. */
+ write_buf(iobuf.out_fd, ff_xb.buf, len);
+ }
+ }
+}
+
+void reduce_iobuf_size(xbuf *out, size_t new_size)
+{
+ if (new_size < out->size) {
+ /* Avoid weird buffer interactions by only outputting this to stderr. */
+ if (msgs2stderr && DEBUG_GTE(IO, 4)) {
+ const char *name = out == &iobuf.out ? "iobuf.out"
+ : out == &iobuf.msg ? "iobuf.msg"
+ : NULL;
+ if (name) {
+ rprintf(FINFO, "[%s] reduced size of %s (-%d)\n",
+ who_am_i(), name, (int)(out->size - new_size));
+ }
+ }
+ out->size = new_size;
+ }
+}
+
+void restore_iobuf_size(xbuf *out)
+{
+ if (IOBUF_WAS_REDUCED(out->size)) {
+ size_t new_size = IOBUF_RESTORE_SIZE(out->size);
+ /* Avoid weird buffer interactions by only outputting this to stderr. */
+ if (msgs2stderr && DEBUG_GTE(IO, 4)) {
+ const char *name = out == &iobuf.out ? "iobuf.out"
+ : out == &iobuf.msg ? "iobuf.msg"
+ : NULL;
+ if (name) {
+ rprintf(FINFO, "[%s] restored size of %s (+%d)\n",
+ who_am_i(), name, (int)(new_size - out->size));
+ }
+ }
+ out->size = new_size;
+ }
+}
+
+static void handle_kill_signal(BOOL flush_ok)
+{
+ got_kill_signal = -1;
+ flush_ok_after_signal = flush_ok;
+ exit_cleanup(RERR_SIGNAL);
+}
+
+/* Perform buffered input and/or output until specified conditions are met.
+ * When given a "needed" read or write request, this returns without doing any
+ * I/O if the needed input bytes or write space is already available. Once I/O
+ * is needed, this will try to do whatever reading and/or writing is currently
+ * possible, up to the maximum buffer allowances, no matter if this is a read
+ * or write request. However, the I/O stops as soon as the required input
+ * bytes or output space is available. If this is not a read request, the
+ * routine may also do some advantageous reading of messages from a multiplexed
+ * input source (which ensures that we don't jam up with everyone in their
+ * "need to write" code and nobody reading the accumulated data that would make
+ * writing possible).
+ *
+ * The iobuf.in, .out and .msg buffers are all circular. Callers need to be
+ * aware that some data copies will need to be split when the bytes wrap around
+ * from the end to the start. In order to help make writing into the output
+ * buffers easier for some operations (such as the use of SIVAL() into the
+ * buffer) a buffer may be temporarily shortened by a small amount, but the
+ * original size will be automatically restored when the .pos wraps to the
+ * start. See also the 3 raw_* iobuf vars that are used in the handling of
+ * MSG_DATA bytes as they are read-from/written-into the buffers.
+ *
+ * When writing, we flush data in the following priority order:
+ *
+ * 1. Finish writing any in-progress MSG_DATA sequence from iobuf.out.
+ *
+ * 2. Write out all the messages from the message buf (if iobuf.msg is active).
+ * Yes, this means that a PIO_NEED_OUTROOM call will completely flush any
+ * messages before getting to the iobuf.out flushing (except for rule 1).
+ *
+ * 3. Write out the raw data from iobuf.out, possibly filling in the multiplexed
+ * MSG_DATA header that was pre-allocated (when output is multiplexed).
+ *
+ * TODO: items for possible future work:
+ *
+ * - Make this routine able to read the generator-to-receiver batch flow?
+ *
+ * Unlike the old routines that this replaces, it is OK to read ahead as far as
+ * we can because the read_a_msg() routine now reads its bytes out of the input
+ * buffer. In the old days, only raw data was in the input buffer, and any
+ * unused raw data in the buf would prevent the reading of socket data. */
+static char *perform_io(size_t needed, int flags)
+{
+ fd_set r_fds, e_fds, w_fds;
+ struct timeval tv;
+ int cnt, max_fd;
+ size_t empty_buf_len = 0;
+ xbuf *out;
+ char *data;
+
+ if (iobuf.in.len == 0 && iobuf.in.pos != 0) {
+ if (iobuf.raw_input_ends_before)
+ iobuf.raw_input_ends_before -= iobuf.in.pos;
+ iobuf.in.pos = 0;
+ }
+
+ switch (flags & PIO_NEED_FLAGS) {
+ case PIO_NEED_INPUT:
+ /* We never resize the circular input buffer. */
+ if (iobuf.in.size < needed) {
+ rprintf(FERROR, "need to read %ld bytes, iobuf.in.buf is only %ld bytes.\n",
+ (long)needed, (long)iobuf.in.size);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ if (msgs2stderr && DEBUG_GTE(IO, 3)) {
+ rprintf(FINFO, "[%s] perform_io(%ld, %sinput)\n",
+ who_am_i(), (long)needed, flags & PIO_CONSUME_INPUT ? "consume&" : "");
+ }
+ break;
+
+ case PIO_NEED_OUTROOM:
+ /* We never resize the circular output buffer. */
+ if (iobuf.out.size - iobuf.out_empty_len < needed) {
+ fprintf(stderr, "need to write %ld bytes, iobuf.out.buf is only %ld bytes.\n",
+ (long)needed, (long)(iobuf.out.size - iobuf.out_empty_len));
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ if (msgs2stderr && DEBUG_GTE(IO, 3)) {
+ rprintf(FINFO, "[%s] perform_io(%ld, outroom) needs to flush %ld\n",
+ who_am_i(), (long)needed,
+ iobuf.out.len + needed > iobuf.out.size
+ ? (long)(iobuf.out.len + needed - iobuf.out.size) : 0L);
+ }
+ break;
+
+ case PIO_NEED_MSGROOM:
+ /* We never resize the circular message buffer. */
+ if (iobuf.msg.size < needed) {
+ fprintf(stderr, "need to write %ld bytes, iobuf.msg.buf is only %ld bytes.\n",
+ (long)needed, (long)iobuf.msg.size);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ if (msgs2stderr && DEBUG_GTE(IO, 3)) {
+ rprintf(FINFO, "[%s] perform_io(%ld, msgroom) needs to flush %ld\n",
+ who_am_i(), (long)needed,
+ iobuf.msg.len + needed > iobuf.msg.size
+ ? (long)(iobuf.msg.len + needed - iobuf.msg.size) : 0L);
+ }
+ break;
+
+ case 0:
+ if (msgs2stderr && DEBUG_GTE(IO, 3))
+ rprintf(FINFO, "[%s] perform_io(%ld, %d)\n", who_am_i(), (long)needed, flags);
+ break;
+
+ default:
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+
+ while (1) {
+ switch (flags & PIO_NEED_FLAGS) {
+ case PIO_NEED_INPUT:
+ if (iobuf.in.len >= needed)
+ goto double_break;
+ break;
+ case PIO_NEED_OUTROOM:
+ /* Note that iobuf.out_empty_len doesn't factor into this check
+ * because iobuf.out.len already holds any needed header len. */
+ if (iobuf.out.len + needed <= iobuf.out.size)
+ goto double_break;
+ break;
+ case PIO_NEED_MSGROOM:
+ if (iobuf.msg.len + needed <= iobuf.msg.size)
+ goto double_break;
+ break;
+ }
+
+ max_fd = -1;
+
+ FD_ZERO(&r_fds);
+ FD_ZERO(&e_fds);
+ if (iobuf.in_fd >= 0 && iobuf.in.size - iobuf.in.len) {
+ if (!read_batch || batch_fd >= 0) {
+ FD_SET(iobuf.in_fd, &r_fds);
+ FD_SET(iobuf.in_fd, &e_fds);
+ }
+ if (iobuf.in_fd > max_fd)
+ max_fd = iobuf.in_fd;
+ }
+
+ /* Only do more filesfrom processing if there is enough room in the out buffer. */
+ if (ff_forward_fd >= 0 && iobuf.out.size - iobuf.out.len > FILESFROM_BUFLEN*2) {
+ FD_SET(ff_forward_fd, &r_fds);
+ if (ff_forward_fd > max_fd)
+ max_fd = ff_forward_fd;
+ }
+
+ FD_ZERO(&w_fds);
+ if (iobuf.out_fd >= 0) {
+ if (iobuf.raw_flushing_ends_before
+ || (!iobuf.msg.len && iobuf.out.len > iobuf.out_empty_len && !(flags & PIO_NEED_MSGROOM))) {
+ if (OUT_MULTIPLEXED && !iobuf.raw_flushing_ends_before) {
+ /* The iobuf.raw_flushing_ends_before value can point off the end
+ * of the iobuf.out buffer for a while, for easier subtracting. */
+ iobuf.raw_flushing_ends_before = iobuf.out.pos + iobuf.out.len;
+
+ SIVAL(iobuf.out.buf + iobuf.raw_data_header_pos, 0,
+ ((MPLEX_BASE + (int)MSG_DATA)<<24) + iobuf.out.len - 4);
+
+ if (msgs2stderr && DEBUG_GTE(IO, 1)) {
+ rprintf(FINFO, "[%s] send_msg(%d, %ld)\n",
+ who_am_i(), (int)MSG_DATA, (long)iobuf.out.len - 4);
+ }
+
+ /* reserve room for the next MSG_DATA header */
+ iobuf.raw_data_header_pos = iobuf.raw_flushing_ends_before;
+ if (iobuf.raw_data_header_pos >= iobuf.out.size)
+ iobuf.raw_data_header_pos -= iobuf.out.size;
+ else if (iobuf.raw_data_header_pos + 4 > iobuf.out.size) {
+ /* The 4-byte header won't fit at the end of the buffer,
+ * so we'll temporarily reduce the output buffer's size
+ * and put the header at the start of the buffer. */
+ reduce_iobuf_size(&iobuf.out, iobuf.raw_data_header_pos);
+ iobuf.raw_data_header_pos = 0;
+ }
+ /* Yes, it is possible for this to make len > size for a while. */
+ iobuf.out.len += 4;
+ }
+
+ empty_buf_len = iobuf.out_empty_len;
+ out = &iobuf.out;
+ } else if (iobuf.msg.len) {
+ empty_buf_len = 0;
+ out = &iobuf.msg;
+ } else
+ out = NULL;
+ if (out) {
+ FD_SET(iobuf.out_fd, &w_fds);
+ if (iobuf.out_fd > max_fd)
+ max_fd = iobuf.out_fd;
+ }
+ } else
+ out = NULL;
+
+ if (max_fd < 0) {
+ switch (flags & PIO_NEED_FLAGS) {
+ case PIO_NEED_INPUT:
+ iobuf.in.len = 0;
+ if (kluge_around_eof == 2)
+ exit_cleanup(0);
+ if (iobuf.in_fd == -2)
+ whine_about_eof(True);
+ rprintf(FERROR, "error in perform_io: no fd for input.\n");
+ exit_cleanup(RERR_PROTOCOL);
+ case PIO_NEED_OUTROOM:
+ case PIO_NEED_MSGROOM:
+ msgs2stderr = 1;
+ drain_multiplex_messages();
+ if (iobuf.out_fd == -2)
+ whine_about_eof(True);
+ rprintf(FERROR, "error in perform_io: no fd for output.\n");
+ exit_cleanup(RERR_PROTOCOL);
+ default:
+ /* No stated needs, so I guess this is OK. */
+ break;
+ }
+ break;
+ }
+
+ if (got_kill_signal > 0)
+ handle_kill_signal(True);
+
+ if (extra_flist_sending_enabled) {
+ if (file_total - file_old_total < MAX_FILECNT_LOOKAHEAD && IN_MULTIPLEXED_AND_READY)
+ tv.tv_sec = 0;
+ else {
+ extra_flist_sending_enabled = False;
+ tv.tv_sec = select_timeout;
+ }
+ } else
+ tv.tv_sec = select_timeout;
+ tv.tv_usec = 0;
+
+ cnt = select(max_fd + 1, &r_fds, &w_fds, &e_fds, &tv);
+
+ if (cnt <= 0) {
+ if (cnt < 0 && errno == EBADF) {
+ msgs2stderr = 1;
+ exit_cleanup(RERR_SOCKETIO);
+ }
+ if (extra_flist_sending_enabled) {
+ extra_flist_sending_enabled = False;
+ send_extra_file_list(sock_f_out, -1);
+ extra_flist_sending_enabled = !flist_eof;
+ } else
+ check_timeout((flags & PIO_NEED_INPUT) != 0, 0);
+ FD_ZERO(&r_fds); /* Just in case... */
+ FD_ZERO(&w_fds);
+ }
+
+ if (iobuf.in_fd >= 0 && FD_ISSET(iobuf.in_fd, &r_fds)) {
+ size_t len, pos = iobuf.in.pos + iobuf.in.len;
+ int n;
+ if (pos >= iobuf.in.size) {
+ pos -= iobuf.in.size;
+ len = iobuf.in.size - iobuf.in.len;
+ } else
+ len = iobuf.in.size - pos;
+ if ((n = read(iobuf.in_fd, iobuf.in.buf + pos, len)) <= 0) {
+ if (n == 0) {
+ /* Signal that input has become invalid. */
+ if (!read_batch || batch_fd < 0 || am_generator)
+ iobuf.in_fd = -2;
+ batch_fd = -1;
+ continue;
+ }
+ if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)
+ n = 0;
+ else {
+ /* Don't write errors on a dead socket. */
+ if (iobuf.in_fd == sock_f_in) {
+ if (am_sender)
+ msgs2stderr = 1;
+ rsyserr(FERROR_SOCKET, errno, "read error");
+ } else
+ rsyserr(FERROR, errno, "read error");
+ exit_cleanup(RERR_SOCKETIO);
+ }
+ }
+ if (msgs2stderr && DEBUG_GTE(IO, 2))
+ rprintf(FINFO, "[%s] recv=%ld\n", who_am_i(), (long)n);
+
+ if (io_timeout) {
+ last_io_in = time(NULL);
+ if (flags & PIO_NEED_INPUT)
+ maybe_send_keepalive(last_io_in, 0);
+ }
+ stats.total_read += n;
+
+ iobuf.in.len += n;
+ }
+
+ if (out && FD_ISSET(iobuf.out_fd, &w_fds)) {
+ size_t len = iobuf.raw_flushing_ends_before ? iobuf.raw_flushing_ends_before - out->pos : out->len;
+ int n;
+
+ if (bwlimit_writemax && len > bwlimit_writemax)
+ len = bwlimit_writemax;
+
+ if (out->pos + len > out->size)
+ len = out->size - out->pos;
+ if ((n = write(iobuf.out_fd, out->buf + out->pos, len)) <= 0) {
+ if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)
+ n = 0;
+ else {
+ /* Don't write errors on a dead socket. */
+ msgs2stderr = 1;
+ iobuf.out_fd = -2;
+ iobuf.out.len = iobuf.msg.len = iobuf.raw_flushing_ends_before = 0;
+ rsyserr(FERROR_SOCKET, errno, "[%s] write error", who_am_i());
+ drain_multiplex_messages();
+ exit_cleanup(RERR_SOCKETIO);
+ }
+ }
+ if (msgs2stderr && DEBUG_GTE(IO, 2)) {
+ rprintf(FINFO, "[%s] %s sent=%ld\n",
+ who_am_i(), out == &iobuf.out ? "out" : "msg", (long)n);
+ }
+
+ if (io_timeout)
+ last_io_out = time(NULL);
+ stats.total_written += n;
+
+ if (bwlimit_writemax)
+ sleep_for_bwlimit(n);
+
+ if ((out->pos += n) == out->size) {
+ if (iobuf.raw_flushing_ends_before)
+ iobuf.raw_flushing_ends_before -= out->size;
+ out->pos = 0;
+ restore_iobuf_size(out);
+ } else if (out->pos == iobuf.raw_flushing_ends_before)
+ iobuf.raw_flushing_ends_before = 0;
+ if ((out->len -= n) == empty_buf_len) {
+ out->pos = 0;
+ restore_iobuf_size(out);
+ if (empty_buf_len)
+ iobuf.raw_data_header_pos = 0;
+ }
+ }
+
+ if (got_kill_signal > 0)
+ handle_kill_signal(True);
+
+ /* We need to help prevent deadlock by doing what reading
+ * we can whenever we are here trying to write. */
+ if (IN_MULTIPLEXED_AND_READY && !(flags & PIO_NEED_INPUT)) {
+ while (!iobuf.raw_input_ends_before && iobuf.in.len > 512)
+ read_a_msg();
+ if (flist_receiving_enabled && iobuf.in.len > 512)
+ wait_for_receiver(); /* generator only */
+ }
+
+ if (ff_forward_fd >= 0 && FD_ISSET(ff_forward_fd, &r_fds)) {
+ /* This can potentially flush all output and enable
+ * multiplexed output, so keep this last in the loop
+ * and be sure to not cache anything that would break
+ * such a change. */
+ forward_filesfrom_data();
+ }
+ }
+ double_break:
+
+ if (got_kill_signal > 0)
+ handle_kill_signal(True);
+
+ data = iobuf.in.buf + iobuf.in.pos;
+
+ if (flags & PIO_CONSUME_INPUT) {
+ iobuf.in.len -= needed;
+ iobuf.in.pos += needed;
+ if (iobuf.in.pos == iobuf.raw_input_ends_before)
+ iobuf.raw_input_ends_before = 0;
+ if (iobuf.in.pos >= iobuf.in.size) {
+ iobuf.in.pos -= iobuf.in.size;
+ if (iobuf.raw_input_ends_before)
+ iobuf.raw_input_ends_before -= iobuf.in.size;
+ }
+ }
+
+ return data;
+}
+
+static void raw_read_buf(char *buf, size_t len)
+{
+ size_t pos = iobuf.in.pos;
+ char *data = perform_io(len, PIO_INPUT_AND_CONSUME);
+ if (iobuf.in.pos <= pos && len) {
+ size_t siz = len - iobuf.in.pos;
+ memcpy(buf, data, siz);
+ memcpy(buf + siz, iobuf.in.buf, iobuf.in.pos);
+ } else
+ memcpy(buf, data, len);
+}
+
+static int32 raw_read_int(void)
+{
+ char *data, buf[4];
+ if (iobuf.in.size - iobuf.in.pos >= 4)
+ data = perform_io(4, PIO_INPUT_AND_CONSUME);
+ else
+ raw_read_buf(data = buf, 4);
+ return IVAL(data, 0);
+}
+
+void noop_io_until_death(void)
+{
+ char buf[1024];
+
+ if (!iobuf.in.buf || !iobuf.out.buf || iobuf.in_fd < 0 || iobuf.out_fd < 0 || kluge_around_eof)
+ return;
+
+ kluge_around_eof = 2;
+ /* Setting an I/O timeout ensures that if something inexplicably weird
+ * happens, we won't hang around forever. */
+ if (!io_timeout)
+ set_io_timeout(60);
+
+ while (1)
+ read_buf(iobuf.in_fd, buf, sizeof buf);
+}
+
+/* Buffer a message for the multiplexed output stream. Is not used for (normal) MSG_DATA. */
+int send_msg(enum msgcode code, const char *buf, size_t len, int convert)
+{
+ char *hdr;
+ size_t needed, pos;
+ BOOL want_debug = DEBUG_GTE(IO, 1) && convert >= 0 && (msgs2stderr || code != MSG_INFO);
+
+ if (!OUT_MULTIPLEXED)
+ return 0;
+
+ if (want_debug)
+ rprintf(FINFO, "[%s] send_msg(%d, %ld)\n", who_am_i(), (int)code, (long)len);
+
+ /* When checking for enough free space for this message, we need to
+ * make sure that there is space for the 4-byte header, plus we'll
+ * assume that we may waste up to 3 bytes (if the header doesn't fit
+ * at the physical end of the buffer). */
+#ifdef ICONV_OPTION
+ if (convert > 0 && ic_send == (iconv_t)-1)
+ convert = 0;
+ if (convert > 0) {
+ /* Ensuring double-size room leaves space for maximal conversion expansion. */
+ needed = len*2 + 4 + 3;
+ } else
+#endif
+ needed = len + 4 + 3;
+ if (iobuf.msg.len + needed > iobuf.msg.size)
+ perform_io(needed, PIO_NEED_MSGROOM);
+
+ pos = iobuf.msg.pos + iobuf.msg.len; /* Must be set after any flushing. */
+ if (pos >= iobuf.msg.size)
+ pos -= iobuf.msg.size;
+ else if (pos + 4 > iobuf.msg.size) {
+ /* The 4-byte header won't fit at the end of the buffer,
+ * so we'll temporarily reduce the message buffer's size
+ * and put the header at the start of the buffer. */
+ reduce_iobuf_size(&iobuf.msg, pos);
+ pos = 0;
+ }
+ hdr = iobuf.msg.buf + pos;
+
+ iobuf.msg.len += 4; /* Allocate room for the coming header bytes. */
+
+#ifdef ICONV_OPTION
+ if (convert > 0) {
+ xbuf inbuf;
+
+ INIT_XBUF(inbuf, (char*)buf, len, (size_t)-1);
+
+ len = iobuf.msg.len;
+ iconvbufs(ic_send, &inbuf, &iobuf.msg,
+ ICB_INCLUDE_BAD | ICB_INCLUDE_INCOMPLETE | ICB_CIRCULAR_OUT | ICB_INIT);
+ if (inbuf.len > 0) {
+ rprintf(FERROR, "overflowed iobuf.msg buffer in send_msg");
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+ len = iobuf.msg.len - len;
+ } else
+#endif
+ {
+ size_t siz;
+
+ if ((pos += 4) == iobuf.msg.size)
+ pos = 0;
+
+ /* Handle a split copy if we wrap around the end of the circular buffer. */
+ if (pos >= iobuf.msg.pos && (siz = iobuf.msg.size - pos) < len) {
+ memcpy(iobuf.msg.buf + pos, buf, siz);
+ memcpy(iobuf.msg.buf, buf + siz, len - siz);
+ } else
+ memcpy(iobuf.msg.buf + pos, buf, len);
+
+ iobuf.msg.len += len;
+ }
+
+ SIVAL(hdr, 0, ((MPLEX_BASE + (int)code)<<24) + len);
+
+ if (want_debug && convert > 0)
+ rprintf(FINFO, "[%s] converted msg len=%ld\n", who_am_i(), (long)len);
+
+ return 1;
+}
+
+void send_msg_int(enum msgcode code, int num)
+{
+ char numbuf[4];
+
+ if (DEBUG_GTE(IO, 1))
+ rprintf(FINFO, "[%s] send_msg_int(%d, %d)\n", who_am_i(), (int)code, num);
+
+ SIVAL(numbuf, 0, num);
+ send_msg(code, numbuf, 4, -1);
+}
+
+static void got_flist_entry_status(enum festatus status, int ndx)
+{
+ struct file_list *flist = flist_for_ndx(ndx, "got_flist_entry_status");
+
+ if (remove_source_files) {
+ active_filecnt--;
+ active_bytecnt -= F_LENGTH(flist->files[ndx - flist->ndx_start]);
+ }
+
+ if (inc_recurse)
+ flist->in_progress--;
+
+ switch (status) {
+ case FES_SUCCESS:
+ if (remove_source_files)
+ send_msg_int(MSG_SUCCESS, ndx);
+ /* FALL THROUGH */
+ case FES_NO_SEND:
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links) {
+ struct file_struct *file = flist->files[ndx - flist->ndx_start];
+ if (F_IS_HLINKED(file)) {
+ if (status == FES_NO_SEND)
+ flist_ndx_push(&hlink_list, -2); /* indicates a failure follows */
+ flist_ndx_push(&hlink_list, ndx);
+ if (inc_recurse)
+ flist->in_progress++;
+ }
+ }
+#endif
+ break;
+ case FES_REDO:
+ if (read_batch) {
+ if (inc_recurse)
+ flist->in_progress++;
+ break;
+ }
+ if (inc_recurse)
+ flist->to_redo++;
+ flist_ndx_push(&redo_list, ndx);
+ break;
+ }
+}
+
+/* Note the fds used for the main socket (which might really be a pipe
+ * for a local transfer, but we can ignore that). */
+void io_set_sock_fds(int f_in, int f_out)
+{
+ sock_f_in = f_in;
+ sock_f_out = f_out;
+}
+
+void set_io_timeout(int secs)
+{
+ io_timeout = secs;
+ allowed_lull = (io_timeout + 1) / 2;
+
+ if (!io_timeout || allowed_lull > SELECT_TIMEOUT)
+ select_timeout = SELECT_TIMEOUT;
+ else
+ select_timeout = allowed_lull;
+
+ if (read_batch)
+ allowed_lull = 0;
+}
+
+static void check_for_d_option_error(const char *msg)
+{
+ static char rsync263_opts[] = "BCDHIKLPRSTWabceghlnopqrtuvxz";
+ char *colon;
+ int saw_d = 0;
+
+ if (*msg != 'r'
+ || strncmp(msg, REMOTE_OPTION_ERROR, sizeof REMOTE_OPTION_ERROR - 1) != 0)
+ return;
+
+ msg += sizeof REMOTE_OPTION_ERROR - 1;
+ if (*msg == '-' || (colon = strchr(msg, ':')) == NULL
+ || strncmp(colon, REMOTE_OPTION_ERROR2, sizeof REMOTE_OPTION_ERROR2 - 1) != 0)
+ return;
+
+ for ( ; *msg != ':'; msg++) {
+ if (*msg == 'd')
+ saw_d = 1;
+ else if (*msg == 'e')
+ break;
+ else if (strchr(rsync263_opts, *msg) == NULL)
+ return;
+ }
+
+ if (saw_d) {
+ rprintf(FWARNING,
+ "*** Try using \"--old-d\" if remote rsync is <= 2.6.3 ***\n");
+ }
+}
+
+/* This is used by the generator to limit how many file transfers can
+ * be active at once when --remove-source-files is specified. Without
+ * this, sender-side deletions were mostly happening at the end. */
+void increment_active_files(int ndx, int itemizing, enum logcode code)
+{
+ while (1) {
+ /* TODO: tune these limits? */
+ int limit = active_bytecnt >= 128*1024 ? 10 : 50;
+ if (active_filecnt < limit)
+ break;
+ check_for_finished_files(itemizing, code, 0);
+ if (active_filecnt < limit)
+ break;
+ wait_for_receiver();
+ }
+
+ active_filecnt++;
+ active_bytecnt += F_LENGTH(cur_flist->files[ndx - cur_flist->ndx_start]);
+}
+
+int get_redo_num(void)
+{
+ return flist_ndx_pop(&redo_list);
+}
+
+int get_hlink_num(void)
+{
+ return flist_ndx_pop(&hlink_list);
+}
+
+/* When we're the receiver and we have a local --files-from list of names
+ * that needs to be sent over the socket to the sender, we have to do two
+ * things at the same time: send the sender a list of what files we're
+ * processing and read the incoming file+info list from the sender. We do
+ * this by making recv_file_list() call forward_filesfrom_data(), which
+ * will ensure that we forward data to the sender until we get some data
+ * for recv_file_list() to use. */
+void start_filesfrom_forwarding(int fd)
+{
+ if (protocol_version < 31 && OUT_MULTIPLEXED) {
+ /* Older protocols send the files-from data w/o packaging
+ * it in multiplexed I/O packets, so temporarily switch
+ * to buffered I/O to match this behavior. */
+ iobuf.msg.pos = iobuf.msg.len = 0; /* Be extra sure no messages go out. */
+ ff_reenable_multiplex = io_end_multiplex_out(MPLX_TO_BUFFERED);
+ }
+ ff_forward_fd = fd;
+
+ alloc_xbuf(&ff_xb, FILESFROM_BUFLEN);
+}
+
+/* Read a line into the "buf" buffer. */
+int read_line(int fd, char *buf, size_t bufsiz, int flags)
+{
+ char ch, *s, *eob;
+
+#ifdef ICONV_OPTION
+ if (flags & RL_CONVERT && iconv_buf.size < bufsiz)
+ realloc_xbuf(&iconv_buf, bufsiz + 1024);
+#endif
+
+ start:
+#ifdef ICONV_OPTION
+ s = flags & RL_CONVERT ? iconv_buf.buf : buf;
+#else
+ s = buf;
+#endif
+ eob = s + bufsiz - 1;
+ while (1) {
+ /* We avoid read_byte() for files because files can return an EOF. */
+ if (fd == iobuf.in_fd)
+ ch = read_byte(fd);
+ else if (safe_read(fd, &ch, 1) == 0)
+ break;
+ if (flags & RL_EOL_NULLS ? ch == '\0' : (ch == '\r' || ch == '\n')) {
+ /* Skip empty lines if dumping comments. */
+ if (flags & RL_DUMP_COMMENTS && s == buf)
+ continue;
+ break;
+ }
+ if (s < eob)
+ *s++ = ch;
+ }
+ *s = '\0';
+
+ if (flags & RL_DUMP_COMMENTS && (*buf == '#' || *buf == ';'))
+ goto start;
+
+#ifdef ICONV_OPTION
+ if (flags & RL_CONVERT) {
+ xbuf outbuf;
+ INIT_XBUF(outbuf, buf, 0, bufsiz);
+ iconv_buf.pos = 0;
+ iconv_buf.len = s - iconv_buf.buf;
+ iconvbufs(ic_recv, &iconv_buf, &outbuf,
+ ICB_INCLUDE_BAD | ICB_INCLUDE_INCOMPLETE | ICB_INIT);
+ outbuf.buf[outbuf.len] = '\0';
+ return outbuf.len;
+ }
+#endif
+
+ return s - buf;
+}
+
+void read_args(int f_in, char *mod_name, char *buf, size_t bufsiz, int rl_nulls,
+ char ***argv_p, int *argc_p, char **request_p)
+{
+ int maxargs = MAX_ARGS;
+ int dot_pos = 0, argc = 0, request_len = 0;
+ char **argv, *p;
+ int rl_flags = (rl_nulls ? RL_EOL_NULLS : 0);
+
+#ifdef ICONV_OPTION
+ rl_flags |= (protect_args && ic_recv != (iconv_t)-1 ? RL_CONVERT : 0);
+#endif
+
+ if (!(argv = new_array(char *, maxargs)))
+ out_of_memory("read_args");
+ if (mod_name && !protect_args)
+ argv[argc++] = "rsyncd";
+
+ if (request_p)
+ *request_p = NULL;
+
+ while (1) {
+ if (read_line(f_in, buf, bufsiz, rl_flags) == 0)
+ break;
+
+ if (argc == maxargs-1) {
+ maxargs += MAX_ARGS;
+ if (!(argv = realloc_array(argv, char *, maxargs)))
+ out_of_memory("read_args");
+ }
+
+ if (dot_pos) {
+ if (request_p && request_len < 1024) {
+ int len = strlen(buf);
+ if (request_len)
+ request_p[0][request_len++] = ' ';
+ if (!(*request_p = realloc_array(*request_p, char, request_len + len + 1)))
+ out_of_memory("read_args");
+ memcpy(*request_p + request_len, buf, len + 1);
+ request_len += len;
+ }
+ if (mod_name)
+ glob_expand_module(mod_name, buf, &argv, &argc, &maxargs);
+ else
+ glob_expand(buf, &argv, &argc, &maxargs);
+ } else {
+ if (!(p = strdup(buf)))
+ out_of_memory("read_args");
+ argv[argc++] = p;
+ if (*p == '.' && p[1] == '\0')
+ dot_pos = argc;
+ }
+ }
+ argv[argc] = NULL;
+
+ glob_expand(NULL, NULL, NULL, NULL);
+
+ *argc_p = argc;
+ *argv_p = argv;
+}
+
+BOOL io_start_buffering_out(int f_out)
+{
+ if (msgs2stderr && DEBUG_GTE(IO, 2))
+ rprintf(FINFO, "[%s] io_start_buffering_out(%d)\n", who_am_i(), f_out);
+
+ if (iobuf.out.buf) {
+ if (iobuf.out_fd == -1)
+ iobuf.out_fd = f_out;
+ else
+ assert(f_out == iobuf.out_fd);
+ return False;
+ }
+
+ alloc_xbuf(&iobuf.out, ROUND_UP_1024(IO_BUFFER_SIZE * 2));
+ iobuf.out_fd = f_out;
+
+ return True;
+}
+
+BOOL io_start_buffering_in(int f_in)
+{
+ if (msgs2stderr && DEBUG_GTE(IO, 2))
+ rprintf(FINFO, "[%s] io_start_buffering_in(%d)\n", who_am_i(), f_in);
+
+ if (iobuf.in.buf) {
+ if (iobuf.in_fd == -1)
+ iobuf.in_fd = f_in;
+ else
+ assert(f_in == iobuf.in_fd);
+ return False;
+ }
+
+ alloc_xbuf(&iobuf.in, ROUND_UP_1024(IO_BUFFER_SIZE));
+ iobuf.in_fd = f_in;
+
+ return True;
+}
+
+void io_end_buffering_in(BOOL free_buffers)
+{
+ if (msgs2stderr && DEBUG_GTE(IO, 2)) {
+ rprintf(FINFO, "[%s] io_end_buffering_in(IOBUF_%s_BUFS)\n",
+ who_am_i(), free_buffers ? "FREE" : "KEEP");
+ }
+
+ if (free_buffers)
+ free_xbuf(&iobuf.in);
+ else
+ iobuf.in.pos = iobuf.in.len = 0;
+
+ iobuf.in_fd = -1;
+}
+
+void io_end_buffering_out(BOOL free_buffers)
+{
+ if (msgs2stderr && DEBUG_GTE(IO, 2)) {
+ rprintf(FINFO, "[%s] io_end_buffering_out(IOBUF_%s_BUFS)\n",
+ who_am_i(), free_buffers ? "FREE" : "KEEP");
+ }
+
+ io_flush(FULL_FLUSH);
+
+ if (free_buffers) {
+ free_xbuf(&iobuf.out);
+ free_xbuf(&iobuf.msg);
+ }
+
+ iobuf.out_fd = -1;
+}
+
+void maybe_flush_socket(int important)
+{
+ if (flist_eof && iobuf.out.buf && iobuf.out.len > iobuf.out_empty_len
+ && (important || time(NULL) - last_io_out >= 5))
+ io_flush(NORMAL_FLUSH);
+}
+
+/* Older rsync versions used to send either a MSG_NOOP (protocol 30) or a
+ * raw-data-based keep-alive (protocol 29), both of which implied forwarding of
+ * the message through the sender. Since the new timeout method does not need
+ * any forwarding, we just send an empty MSG_DATA message, which works with all
+ * rsync versions. This avoids any message forwarding, and leaves the raw-data
+ * stream alone (since we can never be quite sure if that stream is in the
+ * right state for a keep-alive message). */
+void maybe_send_keepalive(time_t now, int flags)
+{
+ if (flags & MSK_ACTIVE_RECEIVER)
+ last_io_in = now; /* Fudge things when we're working hard on the files. */
+
+ /* Early in the transfer (before the receiver forks) the receiving side doesn't
+ * care if it hasn't sent data in a while as long as it is receiving data (in
+ * fact, a pre-3.1.0 rsync would die if we tried to send it a keep alive during
+ * this time). So, if we're an early-receiving proc, just return and let the
+ * incoming data determine if we timeout. */
+ if (!am_sender && !am_receiver && !am_generator)
+ return;
+
+ if (now - last_io_out >= allowed_lull) {
+ /* The receiver is special: it only sends keep-alive messages if it is
+ * actively receiving data. Otherwise, it lets the generator timeout. */
+ if (am_receiver && now - last_io_in >= io_timeout)
+ return;
+
+ if (!iobuf.msg.len && iobuf.out.len == iobuf.out_empty_len)
+ send_msg(MSG_DATA, "", 0, 0);
+ if (!(flags & MSK_ALLOW_FLUSH)) {
+ /* Let the caller worry about writing out the data. */
+ } else if (iobuf.msg.len)
+ perform_io(iobuf.msg.size - iobuf.msg.len + 1, PIO_NEED_MSGROOM);
+ else if (iobuf.out.len > iobuf.out_empty_len)
+ io_flush(NORMAL_FLUSH);
+ }
+}
+
+void start_flist_forward(int ndx)
+{
+ write_int(iobuf.out_fd, ndx);
+ forward_flist_data = 1;
+}
+
+void stop_flist_forward(void)
+{
+ forward_flist_data = 0;
+}
+
+/* Read a message from a multiplexed source. */
+static void read_a_msg(void)
+{
+ char data[BIGPATHBUFLEN];
+ int tag, val;
+ size_t msg_bytes;
+
+ /* This ensures that perform_io() does not try to do any message reading
+ * until we've read all of the data for this message. We should also
+ * try to avoid calling things that will cause data to be written via
+ * perform_io() prior to this being reset to 1. */
+ iobuf.in_multiplexed = -1;
+
+ tag = raw_read_int();
+
+ msg_bytes = tag & 0xFFFFFF;
+ tag = (tag >> 24) - MPLEX_BASE;
+
+ if (DEBUG_GTE(IO, 1) && msgs2stderr)
+ rprintf(FINFO, "[%s] got msg=%d, len=%ld\n", who_am_i(), (int)tag, (long)msg_bytes);
+
+ switch (tag) {
+ case MSG_DATA:
+ assert(iobuf.raw_input_ends_before == 0);
+ /* Though this does not yet read the data, we do mark where in
+ * the buffer the msg data will end once it is read. It is
+ * possible that this points off the end of the buffer, in
+ * which case the gradual reading of the input stream will
+ * cause this value to wrap around and eventually become real. */
+ if (msg_bytes)
+ iobuf.raw_input_ends_before = iobuf.in.pos + msg_bytes;
+ iobuf.in_multiplexed = 1;
+ break;
+ case MSG_STATS:
+ if (msg_bytes != sizeof stats.total_read || !am_generator)
+ goto invalid_msg;
+ raw_read_buf((char*)&stats.total_read, sizeof stats.total_read);
+ iobuf.in_multiplexed = 1;
+ break;
+ case MSG_REDO:
+ if (msg_bytes != 4 || !am_generator)
+ goto invalid_msg;
+ val = raw_read_int();
+ iobuf.in_multiplexed = 1;
+ got_flist_entry_status(FES_REDO, val);
+ break;
+ case MSG_IO_ERROR:
+ if (msg_bytes != 4)
+ goto invalid_msg;
+ val = raw_read_int();
+ iobuf.in_multiplexed = 1;
+ io_error |= val;
+ if (am_receiver)
+ send_msg_int(MSG_IO_ERROR, val);
+ break;
+ case MSG_IO_TIMEOUT:
+ if (msg_bytes != 4 || am_server || am_generator)
+ goto invalid_msg;
+ val = raw_read_int();
+ iobuf.in_multiplexed = 1;
+ if (!io_timeout || io_timeout > val) {
+ if (INFO_GTE(MISC, 2))
+ rprintf(FINFO, "Setting --timeout=%d to match server\n", val);
+ set_io_timeout(val);
+ }
+ break;
+ case MSG_NOOP:
+ /* Support protocol-30 keep-alive method. */
+ if (msg_bytes != 0)
+ goto invalid_msg;
+ iobuf.in_multiplexed = 1;
+ if (am_sender)
+ maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH);
+ break;
+ case MSG_DELETED:
+ if (msg_bytes >= sizeof data)
+ goto overflow;
+ if (am_generator) {
+ raw_read_buf(data, msg_bytes);
+ iobuf.in_multiplexed = 1;
+ send_msg(MSG_DELETED, data, msg_bytes, 1);
+ break;
+ }
+#ifdef ICONV_OPTION
+ if (ic_recv != (iconv_t)-1) {
+ xbuf outbuf, inbuf;
+ char ibuf[512];
+ int add_null = 0;
+ int flags = ICB_INCLUDE_BAD | ICB_INIT;
+
+ INIT_CONST_XBUF(outbuf, data);
+ INIT_XBUF(inbuf, ibuf, 0, (size_t)-1);
+
+ while (msg_bytes) {
+ size_t len = msg_bytes > sizeof ibuf - inbuf.len ? sizeof ibuf - inbuf.len : msg_bytes;
+ raw_read_buf(ibuf + inbuf.len, len);
+ inbuf.pos = 0;
+ inbuf.len += len;
+ if (!(msg_bytes -= len) && !ibuf[inbuf.len-1])
+ inbuf.len--, add_null = 1;
+ if (iconvbufs(ic_send, &inbuf, &outbuf, flags) < 0) {
+ if (errno == E2BIG)
+ goto overflow;
+ /* Buffer ended with an incomplete char, so move the
+ * bytes to the start of the buffer and continue. */
+ memmove(ibuf, ibuf + inbuf.pos, inbuf.len);
+ }
+ flags &= ~ICB_INIT;
+ }
+ if (add_null) {
+ if (outbuf.len == outbuf.size)
+ goto overflow;
+ outbuf.buf[outbuf.len++] = '\0';
+ }
+ msg_bytes = outbuf.len;
+ } else
+#endif
+ raw_read_buf(data, msg_bytes);
+ iobuf.in_multiplexed = 1;
+ /* A directory name was sent with the trailing null */
+ if (msg_bytes > 0 && !data[msg_bytes-1])
+ log_delete(data, S_IFDIR);
+ else {
+ data[msg_bytes] = '\0';
+ log_delete(data, S_IFREG);
+ }
+ break;
+ case MSG_SUCCESS:
+ if (msg_bytes != 4) {
+ invalid_msg:
+ rprintf(FERROR, "invalid multi-message %d:%lu [%s%s]\n",
+ tag, (unsigned long)msg_bytes, who_am_i(),
+ inc_recurse ? "/inc" : "");
+ exit_cleanup(RERR_STREAMIO);
+ }
+ val = raw_read_int();
+ iobuf.in_multiplexed = 1;
+ if (am_generator)
+ got_flist_entry_status(FES_SUCCESS, val);
+ else
+ successful_send(val);
+ break;
+ case MSG_NO_SEND:
+ if (msg_bytes != 4)
+ goto invalid_msg;
+ val = raw_read_int();
+ iobuf.in_multiplexed = 1;
+ if (am_generator)
+ got_flist_entry_status(FES_NO_SEND, val);
+ else
+ send_msg_int(MSG_NO_SEND, val);
+ break;
+ case MSG_ERROR_SOCKET:
+ case MSG_ERROR_UTF8:
+ case MSG_CLIENT:
+ case MSG_LOG:
+ if (!am_generator)
+ goto invalid_msg;
+ if (tag == MSG_ERROR_SOCKET)
+ msgs2stderr = 1;
+ /* FALL THROUGH */
+ case MSG_INFO:
+ case MSG_ERROR:
+ case MSG_ERROR_XFER:
+ case MSG_WARNING:
+ if (msg_bytes >= sizeof data) {
+ overflow:
+ rprintf(FERROR,
+ "multiplexing overflow %d:%lu [%s%s]\n",
+ tag, (unsigned long)msg_bytes, who_am_i(),
+ inc_recurse ? "/inc" : "");
+ exit_cleanup(RERR_STREAMIO);
+ }
+ raw_read_buf(data, msg_bytes);
+ /* We don't set in_multiplexed value back to 1 before writing this message
+ * because the write might loop back and read yet another message, over and
+ * over again, while waiting for room to put the message in the msg buffer. */
+ rwrite((enum logcode)tag, data, msg_bytes, !am_generator);
+ iobuf.in_multiplexed = 1;
+ if (first_message) {
+ if (list_only && !am_sender && tag == 1 && msg_bytes < sizeof data) {
+ data[msg_bytes] = '\0';
+ check_for_d_option_error(data);
+ }
+ first_message = 0;
+ }
+ break;
+ case MSG_ERROR_EXIT:
+ if (msg_bytes == 4)
+ val = raw_read_int();
+ else if (msg_bytes == 0)
+ val = 0;
+ else
+ goto invalid_msg;
+ iobuf.in_multiplexed = 1;
+ if (DEBUG_GTE(EXIT, 3))
+ rprintf(FINFO, "[%s] got MSG_ERROR_EXIT with %ld bytes\n", who_am_i(), (long)msg_bytes);
+ if (msg_bytes == 0) {
+ if (!am_sender && !am_generator) {
+ if (DEBUG_GTE(EXIT, 3)) {
+ rprintf(FINFO, "[%s] sending MSG_ERROR_EXIT (len 0)\n",
+ who_am_i());
+ }
+ send_msg(MSG_ERROR_EXIT, "", 0, 0);
+ io_flush(FULL_FLUSH);
+ }
+ } else if (protocol_version >= 31) {
+ if (am_generator || am_receiver) {
+ if (DEBUG_GTE(EXIT, 3)) {
+ rprintf(FINFO, "[%s] sending MSG_ERROR_EXIT with exit_code %d\n",
+ who_am_i(), val);
+ }
+ send_msg_int(MSG_ERROR_EXIT, val);
+ } else {
+ if (DEBUG_GTE(EXIT, 3)) {
+ rprintf(FINFO, "[%s] sending MSG_ERROR_EXIT (len 0)\n",
+ who_am_i());
+ }
+ send_msg(MSG_ERROR_EXIT, "", 0, 0);
+ }
+ }
+ /* Send a negative linenum so that we don't end up
+ * with a duplicate exit message. */
+ _exit_cleanup(val, __FILE__, 0 - __LINE__);
+ default:
+ rprintf(FERROR, "unexpected tag %d [%s%s]\n",
+ tag, who_am_i(), inc_recurse ? "/inc" : "");
+ exit_cleanup(RERR_STREAMIO);
+ }
+
+ assert(iobuf.in_multiplexed > 0);
+}
+
+static void drain_multiplex_messages(void)
+{
+ while (IN_MULTIPLEXED_AND_READY && iobuf.in.len) {
+ if (iobuf.raw_input_ends_before) {
+ size_t raw_len = iobuf.raw_input_ends_before - iobuf.in.pos;
+ iobuf.raw_input_ends_before = 0;
+ if (raw_len >= iobuf.in.len) {
+ iobuf.in.len = 0;
+ break;
+ }
+ iobuf.in.len -= raw_len;
+ if ((iobuf.in.pos += raw_len) >= iobuf.in.size)
+ iobuf.in.pos -= iobuf.in.size;
+ }
+ read_a_msg();
+ }
+}
+
+void wait_for_receiver(void)
+{
+ if (!iobuf.raw_input_ends_before)
+ read_a_msg();
+
+ if (iobuf.raw_input_ends_before) {
+ int ndx = read_int(iobuf.in_fd);
+ if (ndx < 0) {
+ switch (ndx) {
+ case NDX_FLIST_EOF:
+ flist_eof = 1;
+ if (DEBUG_GTE(FLIST, 3))
+ rprintf(FINFO, "[%s] flist_eof=1\n", who_am_i());
+ break;
+ case NDX_DONE:
+ msgdone_cnt++;
+ break;
+ default:
+ exit_cleanup(RERR_STREAMIO);
+ }
+ } else {
+ struct file_list *flist;
+ flist_receiving_enabled = False;
+ if (DEBUG_GTE(FLIST, 2)) {
+ rprintf(FINFO, "[%s] receiving flist for dir %d\n",
+ who_am_i(), ndx);
+ }
+ flist = recv_file_list(iobuf.in_fd);
+ flist->parent_ndx = ndx;
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links)
+ match_hard_links(flist);
+#endif
+ flist_receiving_enabled = True;
+ }
+ }
+}
+
+unsigned short read_shortint(int f)
+{
+ char b[2];
+ read_buf(f, b, 2);
+ return (UVAL(b, 1) << 8) + UVAL(b, 0);
+}
+
+int32 read_int(int f)
+{
+ char b[4];
+ int32 num;
+
+ read_buf(f, b, 4);
+ num = IVAL(b, 0);
+#if SIZEOF_INT32 > 4
+ if (num & (int32)0x80000000)
+ num |= ~(int32)0xffffffff;
+#endif
+ return num;
+}
+
+int32 read_varint(int f)
+{
+ union {
+ char b[5];
+ int32 x;
+ } u;
+ uchar ch;
+ int extra;
+
+ u.x = 0;
+ ch = read_byte(f);
+ extra = int_byte_extra[ch / 4];
+ if (extra) {
+ uchar bit = ((uchar)1<<(8-extra));
+ if (extra >= (int)sizeof u.b) {
+ rprintf(FERROR, "Overflow in read_varint()\n");
+ exit_cleanup(RERR_STREAMIO);
+ }
+ read_buf(f, u.b, extra);
+ u.b[extra] = ch & (bit-1);
+ } else
+ u.b[0] = ch;
+#if CAREFUL_ALIGNMENT
+ u.x = IVAL(u.b,0);
+#endif
+#if SIZEOF_INT32 > 4
+ if (u.x & (int32)0x80000000)
+ u.x |= ~(int32)0xffffffff;
+#endif
+ return u.x;
+}
+
+int64 read_varlong(int f, uchar min_bytes)
+{
+ union {
+ char b[9];
+ int64 x;
+ } u;
+ char b2[8];
+ int extra;
+
+#if SIZEOF_INT64 < 8
+ memset(u.b, 0, 8);
+#else
+ u.x = 0;
+#endif
+ read_buf(f, b2, min_bytes);
+ memcpy(u.b, b2+1, min_bytes-1);
+ extra = int_byte_extra[CVAL(b2, 0) / 4];
+ if (extra) {
+ uchar bit = ((uchar)1<<(8-extra));
+ if (min_bytes + extra > (int)sizeof u.b) {
+ rprintf(FERROR, "Overflow in read_varlong()\n");
+ exit_cleanup(RERR_STREAMIO);
+ }
+ read_buf(f, u.b + min_bytes - 1, extra);
+ u.b[min_bytes + extra - 1] = CVAL(b2, 0) & (bit-1);
+#if SIZEOF_INT64 < 8
+ if (min_bytes + extra > 5 || u.b[4] || CVAL(u.b,3) & 0x80) {
+ rprintf(FERROR, "Integer overflow: attempted 64-bit offset\n");
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+#endif
+ } else
+ u.b[min_bytes + extra - 1] = CVAL(b2, 0);
+#if SIZEOF_INT64 < 8
+ u.x = IVAL(u.b,0);
+#elif CAREFUL_ALIGNMENT
+ u.x = IVAL64(u.b,0);
+#endif
+ return u.x;
+}
+
+int64 read_longint(int f)
+{
+#if SIZEOF_INT64 >= 8
+ char b[9];
+#endif
+ int32 num = read_int(f);
+
+ if (num != (int32)0xffffffff)
+ return num;
+
+#if SIZEOF_INT64 < 8
+ rprintf(FERROR, "Integer overflow: attempted 64-bit offset\n");
+ exit_cleanup(RERR_UNSUPPORTED);
+#else
+ read_buf(f, b, 8);
+ return IVAL(b,0) | (((int64)IVAL(b,4))<<32);
+#endif
+}
+
+void read_buf(int f, char *buf, size_t len)
+{
+ if (f != iobuf.in_fd) {
+ if (safe_read(f, buf, len) != len)
+ whine_about_eof(False); /* Doesn't return. */
+ goto batch_copy;
+ }
+
+ if (!IN_MULTIPLEXED) {
+ raw_read_buf(buf, len);
+ total_data_read += len;
+ if (forward_flist_data)
+ write_buf(iobuf.out_fd, buf, len);
+ batch_copy:
+ if (f == write_batch_monitor_in)
+ safe_write(batch_fd, buf, len);
+ return;
+ }
+
+ while (1) {
+ size_t siz;
+
+ while (!iobuf.raw_input_ends_before)
+ read_a_msg();
+
+ siz = MIN(len, iobuf.raw_input_ends_before - iobuf.in.pos);
+ if (siz >= iobuf.in.size)
+ siz = iobuf.in.size;
+ raw_read_buf(buf, siz);
+ total_data_read += siz;
+
+ if (forward_flist_data)
+ write_buf(iobuf.out_fd, buf, siz);
+
+ if (f == write_batch_monitor_in)
+ safe_write(batch_fd, buf, siz);
+
+ if ((len -= siz) == 0)
+ break;
+ buf += siz;
+ }
+}
+
+void read_sbuf(int f, char *buf, size_t len)
+{
+ read_buf(f, buf, len);
+ buf[len] = '\0';
+}
+
+uchar read_byte(int f)
+{
+ uchar c;
+ read_buf(f, (char*)&c, 1);
+ return c;
+}
+
+int read_vstring(int f, char *buf, int bufsize)
+{
+ int len = read_byte(f);
+
+ if (len & 0x80)
+ len = (len & ~0x80) * 0x100 + read_byte(f);
+
+ if (len >= bufsize) {
+ rprintf(FERROR, "over-long vstring received (%d > %d)\n",
+ len, bufsize - 1);
+ return -1;
+ }
+
+ if (len)
+ read_buf(f, buf, len);
+ buf[len] = '\0';
+ return len;
+}
+
+/* Populate a sum_struct with values from the socket. This is
+ * called by both the sender and the receiver. */
+void read_sum_head(int f, struct sum_struct *sum)
+{
+ int32 max_blength = protocol_version < 30 ? OLD_MAX_BLOCK_SIZE : MAX_BLOCK_SIZE;
+ sum->count = read_int(f);
+ if (sum->count < 0) {
+ rprintf(FERROR, "Invalid checksum count %ld [%s]\n",
+ (long)sum->count, who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ sum->blength = read_int(f);
+ if (sum->blength < 0 || sum->blength > max_blength) {
+ rprintf(FERROR, "Invalid block length %ld [%s]\n",
+ (long)sum->blength, who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ sum->s2length = protocol_version < 27 ? csum_length : (int)read_int(f);
+ if (sum->s2length < 0 || sum->s2length > MAX_DIGEST_LEN) {
+ rprintf(FERROR, "Invalid checksum length %d [%s]\n",
+ sum->s2length, who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ sum->remainder = read_int(f);
+ if (sum->remainder < 0 || sum->remainder > sum->blength) {
+ rprintf(FERROR, "Invalid remainder length %ld [%s]\n",
+ (long)sum->remainder, who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
+}
+
+/* Send the values from a sum_struct over the socket. Set sum to
+ * NULL if there are no checksums to send. This is called by both
+ * the generator and the sender. */
+void write_sum_head(int f, struct sum_struct *sum)
+{
+ static struct sum_struct null_sum;
+
+ if (sum == NULL)
+ sum = &null_sum;
+
+ write_int(f, sum->count);
+ write_int(f, sum->blength);
+ if (protocol_version >= 27)
+ write_int(f, sum->s2length);
+ write_int(f, sum->remainder);
+}
+
+/* Sleep after writing to limit I/O bandwidth usage.
+ *
+ * @todo Rather than sleeping after each write, it might be better to
+ * use some kind of averaging. The current algorithm seems to always
+ * use a bit less bandwidth than specified, because it doesn't make up
+ * for slow periods. But arguably this is a feature. In addition, we
+ * ought to take the time used to write the data into account.
+ *
+ * During some phases of big transfers (file FOO is uptodate) this is
+ * called with a small bytes_written every time. As the kernel has to
+ * round small waits up to guarantee that we actually wait at least the
+ * requested number of microseconds, this can become grossly inaccurate.
+ * We therefore keep track of the bytes we've written over time and only
+ * sleep when the accumulated delay is at least 1 tenth of a second. */
+static void sleep_for_bwlimit(int bytes_written)
+{
+ static struct timeval prior_tv;
+ static long total_written = 0;
+ struct timeval tv, start_tv;
+ long elapsed_usec, sleep_usec;
+
+#define ONE_SEC 1000000L /* # of microseconds in a second */
+
+ total_written += bytes_written;
+
+ gettimeofday(&start_tv, NULL);
+ if (prior_tv.tv_sec) {
+ elapsed_usec = (start_tv.tv_sec - prior_tv.tv_sec) * ONE_SEC
+ + (start_tv.tv_usec - prior_tv.tv_usec);
+ total_written -= (int64)elapsed_usec * bwlimit / (ONE_SEC/1024);
+ if (total_written < 0)
+ total_written = 0;
+ }
+
+ sleep_usec = total_written * (ONE_SEC/1024) / bwlimit;
+ if (sleep_usec < ONE_SEC / 10) {
+ prior_tv = start_tv;
+ return;
+ }
+
+ tv.tv_sec = sleep_usec / ONE_SEC;
+ tv.tv_usec = sleep_usec % ONE_SEC;
+ select(0, NULL, NULL, NULL, &tv);
+
+ gettimeofday(&prior_tv, NULL);
+ elapsed_usec = (prior_tv.tv_sec - start_tv.tv_sec) * ONE_SEC
+ + (prior_tv.tv_usec - start_tv.tv_usec);
+ total_written = (sleep_usec - elapsed_usec) * bwlimit / (ONE_SEC/1024);
+}
+
+void io_flush(int flush_it_all)
+{
+ if (iobuf.out.len > iobuf.out_empty_len) {
+ if (flush_it_all) /* FULL_FLUSH: flush everything in the output buffers */
+ perform_io(iobuf.out.size - iobuf.out_empty_len, PIO_NEED_OUTROOM);
+ else /* NORMAL_FLUSH: flush at least 1 byte */
+ perform_io(iobuf.out.size - iobuf.out.len + 1, PIO_NEED_OUTROOM);
+ }
+ if (iobuf.msg.len)
+ perform_io(iobuf.msg.size, PIO_NEED_MSGROOM);
+}
+
+void write_shortint(int f, unsigned short x)
+{
+ char b[2];
+ b[0] = (char)x;
+ b[1] = (char)(x >> 8);
+ write_buf(f, b, 2);
+}
+
+void write_int(int f, int32 x)
+{
+ char b[4];
+ SIVAL(b, 0, x);
+ write_buf(f, b, 4);
+}
+
+void write_varint(int f, int32 x)
+{
+ char b[5];
+ uchar bit;
+ int cnt = 4;
+
+ SIVAL(b, 1, x);
+
+ while (cnt > 1 && b[cnt] == 0)
+ cnt--;
+ bit = ((uchar)1<<(7-cnt+1));
+ if (CVAL(b, cnt) >= bit) {
+ cnt++;
+ *b = ~(bit-1);
+ } else if (cnt > 1)
+ *b = b[cnt] | ~(bit*2-1);
+ else
+ *b = b[cnt];
+
+ write_buf(f, b, cnt);
+}
+
+void write_varlong(int f, int64 x, uchar min_bytes)
+{
+ char b[9];
+ uchar bit;
+ int cnt = 8;
+
+#if SIZEOF_INT64 >= 8
+ SIVAL64(b, 1, x);
+#else
+ SIVAL(b, 1, x);
+ if (x <= 0x7FFFFFFF && x >= 0)
+ memset(b + 5, 0, 4);
+ else {
+ rprintf(FERROR, "Integer overflow: attempted 64-bit offset\n");
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+#endif
+
+ while (cnt > min_bytes && b[cnt] == 0)
+ cnt--;
+ bit = ((uchar)1<<(7-cnt+min_bytes));
+ if (CVAL(b, cnt) >= bit) {
+ cnt++;
+ *b = ~(bit-1);
+ } else if (cnt > min_bytes)
+ *b = b[cnt] | ~(bit*2-1);
+ else
+ *b = b[cnt];
+
+ write_buf(f, b, cnt);
+}
+
+/*
+ * Note: int64 may actually be a 32-bit type if ./configure couldn't find any
+ * 64-bit types on this platform.
+ */
+void write_longint(int f, int64 x)
+{
+ char b[12], * const s = b+4;
+
+ SIVAL(s, 0, x);
+ if (x <= 0x7FFFFFFF && x >= 0) {
+ write_buf(f, s, 4);
+ return;
+ }
+
+#if SIZEOF_INT64 < 8
+ rprintf(FERROR, "Integer overflow: attempted 64-bit offset\n");
+ exit_cleanup(RERR_UNSUPPORTED);
+#else
+ memset(b, 0xFF, 4);
+ SIVAL(s, 4, x >> 32);
+ write_buf(f, b, 12);
+#endif
+}
+
+void write_bigbuf(int f, const char *buf, size_t len)
+{
+ size_t half_max = (iobuf.out.size - iobuf.out_empty_len) / 2;
+
+ while (len > half_max + 1024) {
+ write_buf(f, buf, half_max);
+ buf += half_max;
+ len -= half_max;
+ }
+
+ write_buf(f, buf, len);
+}
+
+void write_buf(int f, const char *buf, size_t len)
+{
+ size_t pos, siz;
+
+ if (f != iobuf.out_fd) {
+ safe_write(f, buf, len);
+ goto batch_copy;
+ }
+
+ if (iobuf.out.len + len > iobuf.out.size)
+ perform_io(len, PIO_NEED_OUTROOM);
+
+ pos = iobuf.out.pos + iobuf.out.len; /* Must be set after any flushing. */
+ if (pos >= iobuf.out.size)
+ pos -= iobuf.out.size;
+
+ /* Handle a split copy if we wrap around the end of the circular buffer. */
+ if (pos >= iobuf.out.pos && (siz = iobuf.out.size - pos) < len) {
+ memcpy(iobuf.out.buf + pos, buf, siz);
+ memcpy(iobuf.out.buf, buf + siz, len - siz);
+ } else
+ memcpy(iobuf.out.buf + pos, buf, len);
+
+ iobuf.out.len += len;
+ total_data_written += len;
+
+ batch_copy:
+ if (f == write_batch_monitor_out)
+ safe_write(batch_fd, buf, len);
+}
+
+/* Write a string to the connection */
+void write_sbuf(int f, const char *buf)
+{
+ write_buf(f, buf, strlen(buf));
+}
+
+void write_byte(int f, uchar c)
+{
+ write_buf(f, (char *)&c, 1);
+}
+
+void write_vstring(int f, const char *str, int len)
+{
+ uchar lenbuf[3], *lb = lenbuf;
+
+ if (len > 0x7F) {
+ if (len > 0x7FFF) {
+ rprintf(FERROR,
+ "attempting to send over-long vstring (%d > %d)\n",
+ len, 0x7FFF);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ *lb++ = len / 0x100 + 0x80;
+ }
+ *lb = len;
+
+ write_buf(f, (char*)lenbuf, lb - lenbuf + 1);
+ if (len)
+ write_buf(f, str, len);
+}
+
+/* Send a file-list index using a byte-reduction method. */
+void write_ndx(int f, int32 ndx)
+{
+ static int32 prev_positive = -1, prev_negative = 1;
+ int32 diff, cnt = 0;
+ char b[6];
+
+ if (protocol_version < 30 || read_batch) {
+ write_int(f, ndx);
+ return;
+ }
+
+ /* Send NDX_DONE as a single-byte 0 with no side effects. Send
+ * negative nums as a positive after sending a leading 0xFF. */
+ if (ndx >= 0) {
+ diff = ndx - prev_positive;
+ prev_positive = ndx;
+ } else if (ndx == NDX_DONE) {
+ *b = 0;
+ write_buf(f, b, 1);
+ return;
+ } else {
+ b[cnt++] = (char)0xFF;
+ ndx = -ndx;
+ diff = ndx - prev_negative;
+ prev_negative = ndx;
+ }
+
+ /* A diff of 1 - 253 is sent as a one-byte diff; a diff of 254 - 32767
+ * or 0 is sent as a 0xFE + a two-byte diff; otherwise we send 0xFE
+ * & all 4 bytes of the (non-negative) num with the high-bit set. */
+ if (diff < 0xFE && diff > 0)
+ b[cnt++] = (char)diff;
+ else if (diff < 0 || diff > 0x7FFF) {
+ b[cnt++] = (char)0xFE;
+ b[cnt++] = (char)((ndx >> 24) | 0x80);
+ b[cnt++] = (char)ndx;
+ b[cnt++] = (char)(ndx >> 8);
+ b[cnt++] = (char)(ndx >> 16);
+ } else {
+ b[cnt++] = (char)0xFE;
+ b[cnt++] = (char)(diff >> 8);
+ b[cnt++] = (char)diff;
+ }
+ write_buf(f, b, cnt);
+}
+
+/* Receive a file-list index using a byte-reduction method. */
+int32 read_ndx(int f)
+{
+ static int32 prev_positive = -1, prev_negative = 1;
+ int32 *prev_ptr, num;
+ char b[4];
+
+ if (protocol_version < 30)
+ return read_int(f);
+
+ read_buf(f, b, 1);
+ if (CVAL(b, 0) == 0xFF) {
+ read_buf(f, b, 1);
+ prev_ptr = &prev_negative;
+ } else if (CVAL(b, 0) == 0)
+ return NDX_DONE;
+ else
+ prev_ptr = &prev_positive;
+ if (CVAL(b, 0) == 0xFE) {
+ read_buf(f, b, 2);
+ if (CVAL(b, 0) & 0x80) {
+ b[3] = CVAL(b, 0) & ~0x80;
+ b[0] = b[1];
+ read_buf(f, b+1, 2);
+ num = IVAL(b, 0);
+ } else
+ num = (UVAL(b,0)<<8) + UVAL(b,1) + *prev_ptr;
+ } else
+ num = UVAL(b, 0) + *prev_ptr;
+ *prev_ptr = num;
+ if (prev_ptr == &prev_negative)
+ num = -num;
+ return num;
+}
+
+/* Read a line of up to bufsiz-1 characters into buf. Strips
+ * the (required) trailing newline and all carriage returns.
+ * Returns 1 for success; 0 for I/O error or truncation. */
+int read_line_old(int fd, char *buf, size_t bufsiz, int eof_ok)
+{
+ assert(fd != iobuf.in_fd);
+ bufsiz--; /* leave room for the null */
+ while (bufsiz > 0) {
+ if (safe_read(fd, buf, 1) == 0) {
+ if (eof_ok)
+ break;
+ return 0;
+ }
+ if (*buf == '\0')
+ return 0;
+ if (*buf == '\n')
+ break;
+ if (*buf != '\r') {
+ buf++;
+ bufsiz--;
+ }
+ }
+ *buf = '\0';
+ return bufsiz > 0;
+}
+
+void io_printf(int fd, const char *format, ...)
+{
+ va_list ap;
+ char buf[BIGPATHBUFLEN];
+ int len;
+
+ va_start(ap, format);
+ len = vsnprintf(buf, sizeof buf, format, ap);
+ va_end(ap);
+
+ if (len < 0)
+ exit_cleanup(RERR_PROTOCOL);
+
+ if (len > (int)sizeof buf) {
+ rprintf(FERROR, "io_printf() was too long for the buffer.\n");
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ write_sbuf(fd, buf);
+}
+
+/* Setup for multiplexing a MSG_* stream with the data stream. */
+void io_start_multiplex_out(int fd)
+{
+ io_flush(FULL_FLUSH);
+
+ if (msgs2stderr && DEBUG_GTE(IO, 2))
+ rprintf(FINFO, "[%s] io_start_multiplex_out(%d)\n", who_am_i(), fd);
+
+ if (!iobuf.msg.buf)
+ alloc_xbuf(&iobuf.msg, ROUND_UP_1024(IO_BUFFER_SIZE));
+
+ iobuf.out_empty_len = 4; /* See also OUT_MULTIPLEXED */
+ io_start_buffering_out(fd);
+ got_kill_signal = 0;
+
+ iobuf.raw_data_header_pos = iobuf.out.pos + iobuf.out.len;
+ iobuf.out.len += 4;
+}
+
+/* Setup for multiplexing a MSG_* stream with the data stream. */
+void io_start_multiplex_in(int fd)
+{
+ if (msgs2stderr && DEBUG_GTE(IO, 2))
+ rprintf(FINFO, "[%s] io_start_multiplex_in(%d)\n", who_am_i(), fd);
+
+ iobuf.in_multiplexed = 1; /* See also IN_MULTIPLEXED */
+ io_start_buffering_in(fd);
+}
+
+int io_end_multiplex_in(int mode)
+{
+ int ret = iobuf.in_multiplexed ? iobuf.in_fd : -1;
+
+ if (msgs2stderr && DEBUG_GTE(IO, 2))
+ rprintf(FINFO, "[%s] io_end_multiplex_in(mode=%d)\n", who_am_i(), mode);
+
+ iobuf.in_multiplexed = 0;
+ if (mode == MPLX_SWITCHING)
+ iobuf.raw_input_ends_before = 0;
+ else
+ assert(iobuf.raw_input_ends_before == 0);
+ if (mode != MPLX_TO_BUFFERED)
+ io_end_buffering_in(mode);
+
+ return ret;
+}
+
+int io_end_multiplex_out(int mode)
+{
+ int ret = iobuf.out_empty_len ? iobuf.out_fd : -1;
+
+ if (msgs2stderr && DEBUG_GTE(IO, 2))
+ rprintf(FINFO, "[%s] io_end_multiplex_out(mode=%d)\n", who_am_i(), mode);
+
+ if (mode != MPLX_TO_BUFFERED)
+ io_end_buffering_out(mode);
+ else
+ io_flush(FULL_FLUSH);
+
+ iobuf.out.len = 0;
+ iobuf.out_empty_len = 0;
+ if (got_kill_signal > 0) /* Just in case... */
+ handle_kill_signal(False);
+ got_kill_signal = -1;
+
+ return ret;
+}
+
+void start_write_batch(int fd)
+{
+ /* Some communication has already taken place, but we don't
+ * enable batch writing until here so that we can write a
+ * canonical record of the communication even though the
+ * actual communication so far depends on whether a daemon
+ * is involved. */
+ write_int(batch_fd, protocol_version);
+ if (protocol_version >= 30)
+ write_byte(batch_fd, compat_flags);
+ write_int(batch_fd, checksum_seed);
+
+ if (am_sender)
+ write_batch_monitor_out = fd;
+ else
+ write_batch_monitor_in = fd;
+}
+
+void stop_write_batch(void)
+{
+ write_batch_monitor_out = -1;
+ write_batch_monitor_in = -1;
+}
diff --git a/rsync/io.h b/rsync/io.h
new file mode 100644
index 0000000..14c8489
--- /dev/null
+++ b/rsync/io.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2007-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+extern int protocol_version;
+
+static inline int32
+read_varint30(int f)
+{
+ if (protocol_version < 30)
+ return read_int(f);
+ return read_varint(f);
+}
+
+static inline int64
+read_varlong30(int f, uchar min_bytes)
+{
+ if (protocol_version < 30)
+ return read_longint(f);
+ return read_varlong(f, min_bytes);
+}
+
+static inline void
+write_varint30(int f, int32 x)
+{
+ if (protocol_version < 30)
+ write_int(f, x);
+ else
+ write_varint(f, x);
+}
+
+static inline void
+write_varlong30(int f, int64 x, uchar min_bytes)
+{
+ if (protocol_version < 30)
+ write_longint(f, x);
+ else
+ write_varlong(f, x, min_bytes);
+}
diff --git a/rsync/itypes.h b/rsync/itypes.h
new file mode 100644
index 0000000..3949087
--- /dev/null
+++ b/rsync/itypes.h
@@ -0,0 +1,59 @@
+/* Inline functions for rsync.
+ *
+ * Copyright (C) 2007-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+static inline int
+isDigit(const char *ptr)
+{
+ return isdigit(*(unsigned char *)ptr);
+}
+
+static inline int
+isPrint(const char *ptr)
+{
+ return isprint(*(unsigned char *)ptr);
+}
+
+static inline int
+isSpace(const char *ptr)
+{
+ return isspace(*(unsigned char *)ptr);
+}
+
+static inline int
+isLower(const char *ptr)
+{
+ return islower(*(unsigned char *)ptr);
+}
+
+static inline int
+isUpper(const char *ptr)
+{
+ return isupper(*(unsigned char *)ptr);
+}
+
+static inline int
+toLower(const char *ptr)
+{
+ return tolower(*(unsigned char *)ptr);
+}
+
+static inline int
+toUpper(const char *ptr)
+{
+ return toupper(*(unsigned char *)ptr);
+}
diff --git a/rsync/lib/addrinfo.h b/rsync/lib/addrinfo.h
new file mode 100644
index 0000000..ee9f672
--- /dev/null
+++ b/rsync/lib/addrinfo.h
@@ -0,0 +1,180 @@
+/*
+PostgreSQL Database Management System
+(formerly known as Postgres, then as Postgres95)
+
+Portions Copyright (c) 1996-2005, The PostgreSQL Global Development Group
+
+Portions Copyright (c) 1994, The Regents of the University of California
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose, without fee, and without a written agreement
+is hereby granted, provided that the above copyright notice and this paragraph
+and the following two paragraphs appear in all copies.
+
+IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS
+TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+
+*/
+
+/*-------------------------------------------------------------------------
+ *
+ * getaddrinfo.h
+ * Support getaddrinfo() on platforms that don't have it.
+ *
+ * Note: we use our own routines on platforms that don't HAVE_STRUCT_ADDRINFO,
+ * whether or not the library routine getaddrinfo() can be found. This
+ * policy is needed because on some platforms a manually installed libbind.a
+ * may provide getaddrinfo(), yet the system headers may not provide the
+ * struct definitions needed to call it. To avoid conflict with the libbind
+ * definition in such cases, we rename our routines to pg_xxx() via macros.
+ *
+ * This code will also work on platforms where struct addrinfo is defined
+ * in the system headers but no getaddrinfo() can be located.
+ *
+ * Copyright (c) 2003-2007, PostgreSQL Global Development Group
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef ADDRINFO_H
+#define ADDRINFO_H
+
+
+/* Various macros that ought to be in , but might not be */
+
+#ifndef EAI_FAIL
+#define EAI_BADFLAGS (-1)
+#define EAI_NONAME (-2)
+#define EAI_AGAIN (-3)
+#define EAI_FAIL (-4)
+#define EAI_FAMILY (-6)
+#define EAI_SOCKTYPE (-7)
+#define EAI_SERVICE (-8)
+#define EAI_MEMORY (-10)
+#define EAI_SYSTEM (-11)
+#endif /* !EAI_FAIL */
+
+#ifndef AI_PASSIVE
+#define AI_PASSIVE 0x0001
+#endif
+
+#ifndef AI_NUMERICHOST
+/*
+ * some platforms don't support AI_NUMERICHOST; define as zero if using
+ * the system version of getaddrinfo...
+ */
+#if defined(HAVE_STRUCT_ADDRINFO) && defined(HAVE_GETADDRINFO)
+#define AI_NUMERICHOST 0
+#else
+#define AI_NUMERICHOST 0x0004
+#endif
+#endif
+
+#ifndef AI_CANONNAME
+#if defined(HAVE_STRUCT_ADDRINFO) && defined(HAVE_GETADDRINFO)
+#define AI_CANONNAME 0
+#else
+#define AI_CANONNAME 0x0008
+#endif
+#endif
+
+#ifndef AI_NUMERICSERV
+#if defined(HAVE_STRUCT_ADDRINFO) && defined(HAVE_GETADDRINFO)
+#define AI_NUMERICSERV 0
+#else
+#define AI_NUMERICSERV 0x0010
+#endif
+#endif
+
+#ifndef NI_NUMERICHOST
+#define NI_NUMERICHOST 1
+#endif
+
+#ifndef NI_NUMERICSERV
+#define NI_NUMERICSERV 2
+#endif
+
+#ifndef NI_NOFQDN
+#define NI_NOFQDN 4
+#endif
+
+#ifndef NI_NAMEREQD
+#define NI_NAMEREQD 8
+#endif
+
+#ifndef NI_DGRAM
+#define NI_DGRAM 16
+#endif
+
+
+#ifndef NI_MAXHOST
+#define NI_MAXHOST 1025
+#endif
+
+#ifndef NI_MAXSERV
+#define NI_MAXSERV 32
+#endif
+
+#ifndef HAVE_STRUCT_ADDRINFO
+struct addrinfo
+{
+ int ai_flags;
+ int ai_family;
+ int ai_socktype;
+ int ai_protocol;
+ size_t ai_addrlen;
+ struct sockaddr *ai_addr;
+ char *ai_canonname;
+ struct addrinfo *ai_next;
+};
+#endif /* !HAVE_STRUCT_ADDRINFO */
+
+#ifndef HAVE_STRUCT_SOCKADDR_STORAGE
+struct sockaddr_storage {
+ unsigned short ss_family;
+ unsigned long ss_align;
+ char ss_padding[128 - sizeof (unsigned long)];
+};
+#endif /* !HAVE_STRUCT_SOCKADDR_STORAGE */
+
+#ifndef HAVE_GETADDRINFO
+
+/* Rename private copies per comments above */
+#ifdef getaddrinfo
+#undef getaddrinfo
+#endif
+#define getaddrinfo pg_getaddrinfo
+
+#ifdef freeaddrinfo
+#undef freeaddrinfo
+#endif
+#define freeaddrinfo pg_freeaddrinfo
+
+#ifdef gai_strerror
+#undef gai_strerror
+#endif
+#define gai_strerror pg_gai_strerror
+
+#ifdef getnameinfo
+#undef getnameinfo
+#endif
+#define getnameinfo pg_getnameinfo
+
+extern int getaddrinfo(const char *node, const char *service,
+ const struct addrinfo * hints, struct addrinfo ** res);
+extern void freeaddrinfo(struct addrinfo * res);
+extern const char *gai_strerror(int errcode);
+extern int getnameinfo(const struct sockaddr * sa, socklen_t salen,
+ char *node, size_t nodelen,
+ char *service, size_t servicelen, int flags);
+#endif /* !HAVE_GETADDRINFO */
+
+#endif /* ADDRINFO_H */
diff --git a/rsync/lib/compat.c b/rsync/lib/compat.c
new file mode 100644
index 0000000..dfe963c
--- /dev/null
+++ b/rsync/lib/compat.c
@@ -0,0 +1,275 @@
+/*
+ * Reimplementations of standard functions for platforms that don't have them.
+ *
+ * Copyright (C) 1998 Andrew Tridgell
+ * Copyright (C) 2002 Martin Pool
+ * Copyright (C) 2004-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "itypes.h"
+
+static char number_separator;
+
+#ifndef HAVE_STRDUP
+ char *strdup(char *s)
+{
+ int len = strlen(s) + 1;
+ char *ret = (char *)malloc(len);
+ if (ret)
+ memcpy(ret, s, len);
+ return ret;
+}
+#endif
+
+#ifndef HAVE_GETCWD
+ char *getcwd(char *buf, int size)
+{
+ return getwd(buf);
+}
+#endif
+
+
+#ifndef HAVE_WAITPID
+ pid_t waitpid(pid_t pid, int *statptr, int options)
+{
+#ifdef HAVE_WAIT4
+ return wait4(pid, statptr, options, NULL);
+#else
+ /* If wait4 is also not available, try wait3 for SVR3 variants */
+ /* Less ideal because can't actually request a specific pid */
+ /* At least the WNOHANG option is supported */
+ /* Code borrowed from apache fragment written by dwd@bell-labs.com */
+ int tmp_pid, dummystat;;
+ if (kill(pid, 0) == -1) {
+ errno = ECHILD;
+ return -1;
+ }
+ if (statptr == NULL)
+ statptr = &dummystat;
+ while (((tmp_pid = wait3(statptr, options, 0)) != pid) &&
+ (tmp_pid != -1) && (tmp_pid != 0) && (pid != -1))
+ ;
+ return tmp_pid;
+#endif
+}
+#endif
+
+
+#ifndef HAVE_MEMMOVE
+ void *memmove(void *dest, const void *src, size_t n)
+{
+ bcopy((char *) src, (char *) dest, n);
+ return dest;
+}
+#endif
+
+#ifndef HAVE_STRPBRK
+/**
+ * Find the first ocurrence in @p s of any character in @p accept.
+ *
+ * Derived from glibc
+ **/
+ char *strpbrk(const char *s, const char *accept)
+{
+ while (*s != '\0') {
+ const char *a = accept;
+ while (*a != '\0') {
+ if (*a++ == *s) return (char *)s;
+ }
+ ++s;
+ }
+
+ return NULL;
+}
+#endif
+
+
+#ifndef HAVE_STRLCPY
+/**
+ * Like strncpy but does not 0 fill the buffer and always null
+ * terminates.
+ *
+ * @param bufsize is the size of the destination buffer.
+ *
+ * @return index of the terminating byte.
+ **/
+ size_t strlcpy(char *d, const char *s, size_t bufsize)
+{
+ size_t len = strlen(s);
+ size_t ret = len;
+ if (bufsize > 0) {
+ if (len >= bufsize)
+ len = bufsize-1;
+ memcpy(d, s, len);
+ d[len] = 0;
+ }
+ return ret;
+}
+#endif
+
+#ifndef HAVE_STRLCAT
+/**
+ * Like strncat() but does not 0 fill the buffer and always null
+ * terminates.
+ *
+ * @param bufsize length of the buffer, which should be one more than
+ * the maximum resulting string length.
+ **/
+ size_t strlcat(char *d, const char *s, size_t bufsize)
+{
+ size_t len1 = strlen(d);
+ size_t len2 = strlen(s);
+ size_t ret = len1 + len2;
+
+ if (len1 < bufsize - 1) {
+ if (len2 >= bufsize - len1)
+ len2 = bufsize - len1 - 1;
+ memcpy(d+len1, s, len2);
+ d[len1+len2] = 0;
+ }
+ return ret;
+}
+#endif
+
+/* some systems don't take the 2nd argument */
+int sys_gettimeofday(struct timeval *tv)
+{
+#ifdef HAVE_GETTIMEOFDAY_TZ
+ return gettimeofday(tv, NULL);
+#else
+ return gettimeofday(tv);
+#endif
+}
+
+#define HUMANIFY(mult) \
+ do { \
+ if (num >= mult || num <= -mult) { \
+ double dnum = (double)num / mult; \
+ char units; \
+ if (num < 0) \
+ dnum = -dnum; \
+ if (dnum < mult) \
+ units = 'K'; \
+ else if ((dnum /= mult) < mult) \
+ units = 'M'; \
+ else if ((dnum /= mult) < mult) \
+ units = 'G'; \
+ else { \
+ dnum /= mult; \
+ units = 'T'; \
+ } \
+ if (num < 0) \
+ dnum = -dnum; \
+ snprintf(bufs[n], sizeof bufs[0], "%.2f%c", dnum, units); \
+ return bufs[n]; \
+ } \
+ } while (0)
+
+/* Return the int64 number as a string. If the human_flag arg is non-zero,
+ * we may output the number in K, M, G, or T units. If we don't add a unit
+ * suffix, we will append the fract string, if it is non-NULL. We can
+ * return up to 4 buffers at a time. */
+char *do_big_num(int64 num, int human_flag, const char *fract)
+{
+ static char bufs[4][128]; /* more than enough room */
+ static unsigned int n;
+ char *s;
+ int len, negated;
+
+ if (human_flag && !number_separator) {
+ char buf[32];
+ snprintf(buf, sizeof buf, "%f", 3.14);
+ if (strchr(buf, '.') != NULL)
+ number_separator = ',';
+ else
+ number_separator = '.';
+ }
+
+ n = (n + 1) % (sizeof bufs / sizeof bufs[0]);
+
+ if (human_flag > 1) {
+ if (human_flag == 2)
+ HUMANIFY(1000);
+ else
+ HUMANIFY(1024);
+ }
+
+ s = bufs[n] + sizeof bufs[0] - 1;
+ if (fract) {
+ len = strlen(fract);
+ s -= len;
+ strlcpy(s, fract, len + 1);
+ } else
+ *s = '\0';
+
+ len = 0;
+
+ if (!num)
+ *--s = '0';
+ if (num < 0) {
+ /* A maximum-size negated number can't fit as a positive,
+ * so do one digit in negated form to start us off. */
+ *--s = (char)(-(num % 10)) + '0';
+ num = -(num / 10);
+ len++;
+ negated = 1;
+ } else
+ negated = 0;
+
+ while (num) {
+ if (human_flag) {
+ if (len == 3) {
+ *--s = number_separator;
+ len = 1;
+ } else
+ len++;
+ }
+ *--s = (char)(num % 10) + '0';
+ num /= 10;
+ }
+
+ if (negated)
+ *--s = '-';
+
+ return s;
+}
+
+/* Return the double number as a string. If the human_flag option is > 1,
+ * we may output the number in K, M, G, or T units. The buffer we use for
+ * our result is either a single static buffer defined here, or a buffer
+ * we get from do_big_num(). */
+char *do_big_dnum(double dnum, int human_flag, int decimal_digits)
+{
+ static char tmp_buf[128];
+#if SIZEOF_INT64 >= 8
+ char *fract;
+
+ snprintf(tmp_buf, sizeof tmp_buf, "%.*f", decimal_digits, dnum);
+
+ if (!human_flag || (dnum < 1000.0 && dnum > -1000.0))
+ return tmp_buf;
+
+ for (fract = tmp_buf+1; isDigit(fract); fract++) {}
+
+ return do_big_num((int64)dnum, human_flag, fract);
+#else
+ /* A big number might lose digits converting to a too-short int64,
+ * so let's just return the raw double conversion. */
+ snprintf(tmp_buf, sizeof tmp_buf, "%.*f", decimal_digits, dnum);
+ return tmp_buf;
+#endif
+}
diff --git a/rsync/lib/dummy.in b/rsync/lib/dummy.in
new file mode 100644
index 0000000..3b26a20
--- /dev/null
+++ b/rsync/lib/dummy.in
@@ -0,0 +1,2 @@
+This is a dummy file to ensure that the lib directory gets created
+by configure when a VPATH is used.
diff --git a/rsync/lib/getaddrinfo.c b/rsync/lib/getaddrinfo.c
new file mode 100644
index 0000000..96d7a2b
--- /dev/null
+++ b/rsync/lib/getaddrinfo.c
@@ -0,0 +1,504 @@
+/*
+PostgreSQL Database Management System
+(formerly known as Postgres, then as Postgres95)
+
+Portions Copyright (c) 1996-2005, The PostgreSQL Global Development Group
+
+Portions Copyright (c) 1994, The Regents of the University of California
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose, without fee, and without a written agreement
+is hereby granted, provided that the above copyright notice and this paragraph
+and the following two paragraphs appear in all copies.
+
+IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS
+TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+
+*/
+
+/*-------------------------------------------------------------------------
+ *
+ * getaddrinfo.c
+ * Support getaddrinfo() on platforms that don't have it.
+ *
+ * We also supply getnameinfo() here, assuming that the platform will have
+ * it if and only if it has getaddrinfo(). If this proves false on some
+ * platform, we'll need to split this file and provide a separate configure
+ * test for getnameinfo().
+ *
+ * Copyright (c) 2003-2007, PostgreSQL Global Development Group
+ *
+ * Copyright (C) 2007 Jeremy Allison.
+ * Modified to return multiple IPv4 addresses for Samba.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "rsync.h"
+
+#ifndef SMB_MALLOC
+#define SMB_MALLOC(s) malloc(s)
+#endif
+
+#ifndef SMB_STRDUP
+#define SMB_STRDUP(s) strdup(s)
+#endif
+
+#ifndef HOST_NAME_MAX
+#define HOST_NAME_MAX 255
+#endif
+
+static int check_hostent_err(struct hostent *hp)
+{
+#ifndef INET6
+ extern int h_errno;
+#endif
+ if (!hp) {
+ switch (h_errno) {
+ case HOST_NOT_FOUND:
+ case NO_DATA:
+ return EAI_NONAME;
+ case TRY_AGAIN:
+ return EAI_AGAIN;
+ case NO_RECOVERY:
+ default:
+ return EAI_FAIL;
+ }
+ }
+ if (!hp->h_name || hp->h_addrtype != AF_INET) {
+ return EAI_FAIL;
+ }
+ return 0;
+}
+
+static char *canon_name_from_hostent(struct hostent *hp,
+ int *perr)
+{
+ char *ret = NULL;
+
+ *perr = check_hostent_err(hp);
+ if (*perr) {
+ return NULL;
+ }
+ ret = SMB_STRDUP(hp->h_name);
+ if (!ret) {
+ *perr = EAI_MEMORY;
+ }
+ return ret;
+}
+
+static char *get_my_canon_name(int *perr)
+{
+ char name[HOST_NAME_MAX+1];
+
+ if (gethostname(name, HOST_NAME_MAX) == -1) {
+ *perr = EAI_FAIL;
+ return NULL;
+ }
+ /* Ensure null termination. */
+ name[HOST_NAME_MAX] = '\0';
+ return canon_name_from_hostent(gethostbyname(name), perr);
+}
+
+static char *get_canon_name_from_addr(struct in_addr ip,
+ int *perr)
+{
+ return canon_name_from_hostent(
+ gethostbyaddr((void *)&ip, sizeof ip, AF_INET),
+ perr);
+}
+
+static struct addrinfo *alloc_entry(const struct addrinfo *hints,
+ struct in_addr ip,
+ unsigned short port)
+{
+ struct sockaddr_in *psin = NULL;
+ struct addrinfo *ai = SMB_MALLOC(sizeof(*ai));
+
+ if (!ai) {
+ return NULL;
+ }
+ memset(ai, '\0', sizeof(*ai));
+
+ psin = SMB_MALLOC(sizeof(*psin));
+ if (!psin) {
+ free(ai);
+ return NULL;
+ }
+
+ memset(psin, '\0', sizeof(*psin));
+
+ psin->sin_family = AF_INET;
+ psin->sin_port = htons(port);
+ psin->sin_addr = ip;
+
+ ai->ai_flags = 0;
+ ai->ai_family = AF_INET;
+ ai->ai_socktype = hints->ai_socktype;
+ ai->ai_protocol = hints->ai_protocol;
+ ai->ai_addrlen = sizeof(*psin);
+ ai->ai_addr = (struct sockaddr *) psin;
+ ai->ai_canonname = NULL;
+ ai->ai_next = NULL;
+
+ return ai;
+}
+
+/*
+ * get address info for a single ipv4 address.
+ *
+ * Bugs: - servname can only be a number, not text.
+ */
+
+static int getaddr_info_single_addr(const char *service,
+ uint32 addr,
+ const struct addrinfo *hints,
+ struct addrinfo **res)
+{
+
+ struct addrinfo *ai = NULL;
+ struct in_addr ip;
+ unsigned short port = 0;
+
+ if (service) {
+ port = (unsigned short)atoi(service);
+ }
+ ip.s_addr = htonl(addr);
+
+ ai = alloc_entry(hints, ip, port);
+ if (!ai) {
+ return EAI_MEMORY;
+ }
+
+ /* If we're asked for the canonical name,
+ * make sure it returns correctly. */
+ if (!(hints->ai_flags & AI_NUMERICSERV) &&
+ hints->ai_flags & AI_CANONNAME) {
+ int err;
+ if (addr == INADDR_LOOPBACK || addr == INADDR_ANY) {
+ ai->ai_canonname = get_my_canon_name(&err);
+ } else {
+ ai->ai_canonname =
+ get_canon_name_from_addr(ip,&err);
+ }
+ if (ai->ai_canonname == NULL) {
+ freeaddrinfo(ai);
+ return err;
+ }
+ }
+
+ *res = ai;
+ return 0;
+}
+
+/*
+ * get address info for multiple ipv4 addresses.
+ *
+ * Bugs: - servname can only be a number, not text.
+ */
+
+static int getaddr_info_name(const char *node,
+ const char *service,
+ const struct addrinfo *hints,
+ struct addrinfo **res)
+{
+ struct addrinfo *listp = NULL, *prevp = NULL;
+ char **pptr = NULL;
+ int err;
+ struct hostent *hp = NULL;
+ unsigned short port = 0;
+
+ if (service) {
+ port = (unsigned short)atoi(service);
+ }
+
+ hp = gethostbyname(node);
+ err = check_hostent_err(hp);
+ if (err) {
+ return err;
+ }
+
+ for(pptr = hp->h_addr_list; *pptr; pptr++) {
+ struct in_addr ip = *(struct in_addr *)*pptr;
+ struct addrinfo *ai = alloc_entry(hints, ip, port);
+
+ if (!ai) {
+ freeaddrinfo(listp);
+ return EAI_MEMORY;
+ }
+
+ if (!listp) {
+ listp = ai;
+ prevp = ai;
+ ai->ai_canonname = SMB_STRDUP(hp->h_name);
+ if (!ai->ai_canonname) {
+ freeaddrinfo(listp);
+ return EAI_MEMORY;
+ }
+ } else {
+ prevp->ai_next = ai;
+ prevp = ai;
+ }
+ }
+ *res = listp;
+ return 0;
+}
+
+/*
+ * get address info for ipv4 sockets.
+ *
+ * Bugs: - servname can only be a number, not text.
+ */
+
+int getaddrinfo(const char *node,
+ const char *service,
+ const struct addrinfo * hintp,
+ struct addrinfo ** res)
+{
+ struct addrinfo hints;
+
+ /* Setup the hints struct. */
+ if (hintp == NULL) {
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ } else {
+ memcpy(&hints, hintp, sizeof(hints));
+ }
+
+ if (hints.ai_family != AF_INET && hints.ai_family != AF_UNSPEC) {
+ return EAI_FAMILY;
+ }
+
+ if (hints.ai_socktype == 0) {
+ hints.ai_socktype = SOCK_STREAM;
+ }
+
+ if (!node && !service) {
+ return EAI_NONAME;
+ }
+
+ if (node) {
+ if (node[0] == '\0') {
+ return getaddr_info_single_addr(service,
+ INADDR_ANY,
+ &hints,
+ res);
+ } else if (hints.ai_flags & AI_NUMERICHOST) {
+ struct in_addr ip;
+ if (inet_pton(AF_INET, node, &ip) <= 0)
+ return EAI_FAIL;
+ return getaddr_info_single_addr(service,
+ ntohl(ip.s_addr),
+ &hints,
+ res);
+ } else {
+ return getaddr_info_name(node,
+ service,
+ &hints,
+ res);
+ }
+ } else if (hints.ai_flags & AI_PASSIVE) {
+ return getaddr_info_single_addr(service,
+ INADDR_ANY,
+ &hints,
+ res);
+ }
+ return getaddr_info_single_addr(service,
+ INADDR_LOOPBACK,
+ &hints,
+ res);
+}
+
+
+void freeaddrinfo(struct addrinfo *res)
+{
+ struct addrinfo *next = NULL;
+
+ for (;res; res = next) {
+ next = res->ai_next;
+ if (res->ai_canonname) {
+ free(res->ai_canonname);
+ }
+ if (res->ai_addr) {
+ free(res->ai_addr);
+ }
+ free(res);
+ }
+}
+
+
+const char *gai_strerror(int errcode)
+{
+#ifdef HAVE_HSTRERROR
+ int hcode;
+
+ switch (errcode)
+ {
+ case EAI_NONAME:
+ hcode = HOST_NOT_FOUND;
+ break;
+ case EAI_AGAIN:
+ hcode = TRY_AGAIN;
+ break;
+ case EAI_FAIL:
+ default:
+ hcode = NO_RECOVERY;
+ break;
+ }
+
+ return hstrerror(hcode);
+#else /* !HAVE_HSTRERROR */
+
+ switch (errcode)
+ {
+ case EAI_NONAME:
+ return "Unknown host";
+ case EAI_AGAIN:
+ return "Host name lookup failure";
+#ifdef EAI_BADFLAGS
+ case EAI_BADFLAGS:
+ return "Invalid argument";
+#endif
+#ifdef EAI_FAMILY
+ case EAI_FAMILY:
+ return "Address family not supported";
+#endif
+#ifdef EAI_MEMORY
+ case EAI_MEMORY:
+ return "Not enough memory";
+#endif
+#ifdef EAI_NODATA
+ case EAI_NODATA:
+ return "No host data of that type was found";
+#endif
+#ifdef EAI_SERVICE
+ case EAI_SERVICE:
+ return "Class type not found";
+#endif
+#ifdef EAI_SOCKTYPE
+ case EAI_SOCKTYPE:
+ return "Socket type not supported";
+#endif
+ default:
+ return "Unknown server error";
+ }
+#endif /* HAVE_HSTRERROR */
+}
+
+static int gethostnameinfo(const struct sockaddr *sa,
+ char *node,
+ size_t nodelen,
+ int flags)
+{
+ int ret = -1;
+ char *p = NULL;
+
+ if (!(flags & NI_NUMERICHOST)) {
+ struct hostent *hp = gethostbyaddr(
+ (void *)&((struct sockaddr_in *)sa)->sin_addr,
+ sizeof (struct in_addr),
+ sa->sa_family);
+ ret = check_hostent_err(hp);
+ if (ret == 0) {
+ /* Name looked up successfully. */
+ ret = snprintf(node, nodelen, "%s", hp->h_name);
+ if (ret < 0 || (size_t)ret >= nodelen) {
+ return EAI_MEMORY;
+ }
+ if (flags & NI_NOFQDN) {
+ p = strchr(node,'.');
+ if (p) {
+ *p = '\0';
+ }
+ }
+ return 0;
+ }
+
+ if (flags & NI_NAMEREQD) {
+ /* If we require a name and didn't get one,
+ * automatically fail. */
+ return ret;
+ }
+ /* Otherwise just fall into the numeric host code... */
+ }
+ p = inet_ntoa(((struct sockaddr_in *)sa)->sin_addr);
+ ret = snprintf(node, nodelen, "%s", p);
+ if (ret < 0 || (size_t)ret >= nodelen) {
+ return EAI_MEMORY;
+ }
+ return 0;
+}
+
+static int getservicenameinfo(const struct sockaddr *sa,
+ char *service,
+ size_t servicelen,
+ int flags)
+{
+ int ret = -1;
+ int port = ntohs(((struct sockaddr_in *)sa)->sin_port);
+
+ if (!(flags & NI_NUMERICSERV)) {
+ struct servent *se = getservbyport(
+ port,
+ (flags & NI_DGRAM) ? "udp" : "tcp");
+ if (se && se->s_name) {
+ /* Service name looked up successfully. */
+ ret = snprintf(service, servicelen, "%s", se->s_name);
+ if (ret < 0 || (size_t)ret >= servicelen) {
+ return EAI_MEMORY;
+ }
+ return 0;
+ }
+ /* Otherwise just fall into the numeric service code... */
+ }
+ ret = snprintf(service, servicelen, "%d", port);
+ if (ret < 0 || (size_t)ret >= servicelen) {
+ return EAI_MEMORY;
+ }
+ return 0;
+}
+
+/*
+ * Convert an ipv4 address to a hostname.
+ *
+ * Bugs: - No IPv6 support.
+ */
+int getnameinfo(const struct sockaddr *sa, socklen_t salen,
+ char *node, size_t nodelen,
+ char *service, size_t servicelen, int flags)
+{
+
+ /* Invalid arguments. */
+ if (sa == NULL || (node == NULL && service == NULL)) {
+ return EAI_FAIL;
+ }
+
+ if (sa->sa_family != AF_INET) {
+ return EAI_FAIL;
+ }
+
+ if (salen < (socklen_t)sizeof (struct sockaddr_in)) {
+ return EAI_FAIL;
+ }
+
+ if (node) {
+ int ret = gethostnameinfo(sa, node, nodelen, flags);
+ if (ret)
+ return ret;
+ }
+
+ if (service) {
+ return getservicenameinfo(sa, service, servicelen, flags);
+ }
+ return 0;
+}
diff --git a/rsync/lib/getpass.c b/rsync/lib/getpass.c
new file mode 100644
index 0000000..dea3126
--- /dev/null
+++ b/rsync/lib/getpass.c
@@ -0,0 +1,72 @@
+/*
+ * An implementation of getpass for systems that lack one.
+ *
+ * Copyright (C) 2013 Roman Donchenko
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include
+#include
+#include
+
+#include "rsync.h"
+
+char *getpass(const char *prompt)
+{
+ static char password[256];
+
+ BOOL tty_changed = False, read_success;
+ struct termios tty_old, tty_new;
+ FILE *in = stdin, *out = stderr;
+ FILE *tty = fopen("/dev/tty", "w+");
+
+ if (tty)
+ in = out = tty;
+
+ if (tcgetattr(fileno(in), &tty_old) == 0) {
+ tty_new = tty_old;
+ tty_new.c_lflag &= ~(ECHO | ISIG);
+
+ if (tcsetattr(fileno(in), TCSAFLUSH, &tty_new) == 0)
+ tty_changed = True;
+ }
+
+ if (!tty_changed)
+ fputs("(WARNING: will be visible) ", out);
+ fputs(prompt, out);
+ fflush(out);
+
+ read_success = fgets(password, sizeof password, in) != NULL;
+
+ /* Print the newline that hasn't been echoed. */
+ fputc('\n', out);
+
+ if (tty_changed)
+ tcsetattr(fileno(in), TCSAFLUSH, &tty_old);
+
+ if (tty)
+ fclose(tty);
+
+ if (read_success) {
+ /* Remove the trailing newline. */
+ size_t password_len = strlen(password);
+ if (password_len && password[password_len - 1] == '\n')
+ password[password_len - 1] = '\0';
+
+ return password;
+ }
+
+ return NULL;
+}
diff --git a/rsync/lib/inet_ntop.c b/rsync/lib/inet_ntop.c
new file mode 100644
index 0000000..7be7368
--- /dev/null
+++ b/rsync/lib/inet_ntop.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 1996-2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+#include "rsync.h"
+
+#define NS_INT16SZ 2
+#define NS_IN6ADDRSZ 16
+
+/*
+ * WARNING: Don't even consider trying to compile this on a system where
+ * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
+ */
+
+static const char *inet_ntop4(const unsigned char *src, char *dst,
+ size_t size);
+
+#ifdef AF_INET6
+static const char *inet_ntop6(const unsigned char *src, char *dst,
+ size_t size);
+#endif
+
+/* char *
+ * isc_net_ntop(af, src, dst, size)
+ * convert a network format address to presentation format.
+ * return:
+ * pointer to presentation format address (`dst'), or NULL (see errno).
+ * author:
+ * Paul Vixie, 1996.
+ */
+const char *
+inet_ntop(int af, const void *src, char *dst, size_t size)
+{
+ switch (af) {
+ case AF_INET:
+ return (inet_ntop4(src, dst, size));
+#ifdef AF_INET6
+ case AF_INET6:
+ return (inet_ntop6(src, dst, size));
+#endif
+ default:
+ errno = EAFNOSUPPORT;
+ return (NULL);
+ }
+ /* NOTREACHED */
+}
+
+/* const char *
+ * inet_ntop4(src, dst, size)
+ * format an IPv4 address
+ * return:
+ * `dst' (as a const)
+ * notes:
+ * (1) uses no statics
+ * (2) takes a unsigned char* not an in_addr as input
+ * author:
+ * Paul Vixie, 1996.
+ */
+static const char *
+inet_ntop4(const unsigned char *src, char *dst, size_t size)
+{
+ static const char *fmt = "%u.%u.%u.%u";
+ char tmp[sizeof "255.255.255.255"];
+ size_t len;
+
+ len = snprintf(tmp, sizeof tmp, fmt, src[0], src[1], src[2], src[3]);
+ if (len >= size) {
+ errno = ENOSPC;
+ return (NULL);
+ }
+ memcpy(dst, tmp, len + 1);
+
+ return (dst);
+}
+
+/* const char *
+ * isc_inet_ntop6(src, dst, size)
+ * convert IPv6 binary address into presentation (printable) format
+ * author:
+ * Paul Vixie, 1996.
+ */
+#ifdef AF_INET6
+static const char *
+inet_ntop6(const unsigned char *src, char *dst, size_t size)
+{
+ /*
+ * Note that int32_t and int16_t need only be "at least" large enough
+ * to contain a value of the specified size. On some systems, like
+ * Crays, there is no such thing as an integer variable with 16 bits.
+ * Keep this in mind if you think this function should have been coded
+ * to use pointer overlays. All the world's not a VAX.
+ */
+ char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp;
+ struct { int base, len; } best, cur;
+ unsigned int words[NS_IN6ADDRSZ / NS_INT16SZ];
+ int i, inc;
+
+ /*
+ * Preprocess:
+ * Copy the input (bytewise) array into a wordwise array.
+ * Find the longest run of 0x00's in src[] for :: shorthanding.
+ */
+ memset(words, '\0', sizeof words);
+ for (i = 0; i < NS_IN6ADDRSZ; i++)
+ words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
+ best.base = -1;
+ cur.base = -1;
+ for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
+ if (words[i] == 0) {
+ if (cur.base == -1)
+ cur.base = i, cur.len = 1;
+ else
+ cur.len++;
+ } else {
+ if (cur.base != -1) {
+ if (best.base == -1 || cur.len > best.len)
+ best = cur;
+ cur.base = -1;
+ }
+ }
+ }
+ if (cur.base != -1) {
+ if (best.base == -1 || cur.len > best.len)
+ best = cur;
+ }
+ if (best.base != -1 && best.len < 2)
+ best.base = -1;
+
+ /*
+ * Format the result.
+ */
+ tp = tmp;
+ for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
+ /* Are we inside the best run of 0x00's? */
+ if (best.base != -1 && i >= best.base &&
+ i < (best.base + best.len)) {
+ if (i == best.base)
+ *tp++ = ':';
+ continue;
+ }
+ /* Are we following an initial run of 0x00s or any real hex? */
+ if (i != 0)
+ *tp++ = ':';
+ /* Is this address an encapsulated IPv4? */
+ if (i == 6 && best.base == 0 &&
+ (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) {
+ if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp)))
+ return (NULL);
+ tp += strlen(tp);
+ break;
+ }
+ inc = snprintf(tp, 5, "%x", words[i]);
+ assert(inc < 5);
+ tp += inc;
+ }
+ /* Was it a trailing run of 0x00's? */
+ if (best.base != -1 && (best.base + best.len) ==
+ (NS_IN6ADDRSZ / NS_INT16SZ))
+ *tp++ = ':';
+ *tp++ = '\0';
+
+ /*
+ * Check for overflow, copy, and we're done.
+ */
+ if ((size_t)(tp - tmp) > size) {
+ errno = ENOSPC;
+ return (NULL);
+ }
+ memcpy(dst, tmp, tp - tmp);
+ return (dst);
+}
+#endif /* AF_INET6 */
diff --git a/rsync/lib/inet_pton.c b/rsync/lib/inet_pton.c
new file mode 100644
index 0000000..4a0be88
--- /dev/null
+++ b/rsync/lib/inet_pton.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 1996-2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "rsync.h"
+
+#define NS_INT16SZ 2
+#define NS_INADDRSZ 4
+#define NS_IN6ADDRSZ 16
+
+/*
+ * WARNING: Don't even consider trying to compile this on a system where
+ * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
+ */
+
+static int inet_pton4(const char *src, unsigned char *dst);
+#ifdef INET6
+static int inet_pton6(const char *src, unsigned char *dst);
+#endif
+
+/* int
+ * inet_pton(af, src, dst)
+ * convert from presentation format (which usually means ASCII printable)
+ * to network format (which is usually some kind of binary format).
+ * return:
+ * 1 if the address was valid for the specified address family
+ * 0 if the address wasn't valid (`dst' is untouched in this case)
+ * -1 if some other error occurred (`dst' is untouched in this case, too)
+ * author:
+ * Paul Vixie, 1996.
+ */
+int
+inet_pton(int af,
+ const char *src,
+ void *dst)
+{
+ switch (af) {
+ case AF_INET:
+ return (inet_pton4(src, dst));
+#ifdef INET6
+ case AF_INET6:
+ return (inet_pton6(src, dst));
+#endif
+ default:
+ errno = EAFNOSUPPORT;
+ return (-1);
+ }
+ /* NOTREACHED */
+}
+
+/* int
+ * inet_pton4(src, dst)
+ * like inet_aton() but without all the hexadecimal and shorthand.
+ * return:
+ * 1 if `src' is a valid dotted quad, else 0.
+ * notice:
+ * does not touch `dst' unless it's returning 1.
+ * author:
+ * Paul Vixie, 1996.
+ */
+static int
+inet_pton4(src, dst)
+ const char *src;
+ unsigned char *dst;
+{
+ static const char digits[] = "0123456789";
+ int saw_digit, octets, ch;
+ unsigned char tmp[NS_INADDRSZ], *tp;
+
+ saw_digit = 0;
+ octets = 0;
+ *(tp = tmp) = 0;
+ while ((ch = *src++) != '\0') {
+ const char *pch;
+
+ if ((pch = strchr(digits, ch)) != NULL) {
+ unsigned int new = *tp * 10 + (pch - digits);
+
+ if (new > 255)
+ return (0);
+ *tp = new;
+ if (! saw_digit) {
+ if (++octets > 4)
+ return (0);
+ saw_digit = 1;
+ }
+ } else if (ch == '.' && saw_digit) {
+ if (octets == 4)
+ return (0);
+ *++tp = 0;
+ saw_digit = 0;
+ } else
+ return (0);
+ }
+ if (octets < 4)
+ return (0);
+ memcpy(dst, tmp, NS_INADDRSZ);
+ return (1);
+}
+
+/* int
+ * inet_pton6(src, dst)
+ * convert presentation level address to network order binary form.
+ * return:
+ * 1 if `src' is a valid [RFC1884 2.2] address, else 0.
+ * notice:
+ * (1) does not touch `dst' unless it's returning 1.
+ * (2) :: in a full address is silently ignored.
+ * credit:
+ * inspired by Mark Andrews.
+ * author:
+ * Paul Vixie, 1996.
+ */
+#ifdef INET6
+static int
+inet_pton6(src, dst)
+ const char *src;
+ unsigned char *dst;
+{
+ static const char xdigits_l[] = "0123456789abcdef",
+ xdigits_u[] = "0123456789ABCDEF";
+ unsigned char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
+ const char *xdigits, *curtok;
+ int ch, saw_xdigit;
+ unsigned int val;
+
+ memset((tp = tmp), '\0', NS_IN6ADDRSZ);
+ endp = tp + NS_IN6ADDRSZ;
+ colonp = NULL;
+ /* Leading :: requires some special handling. */
+ if (*src == ':')
+ if (*++src != ':')
+ return (0);
+ curtok = src;
+ saw_xdigit = 0;
+ val = 0;
+ while ((ch = *src++) != '\0') {
+ const char *pch;
+
+ if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
+ pch = strchr((xdigits = xdigits_u), ch);
+ if (pch != NULL) {
+ val <<= 4;
+ val |= (pch - xdigits);
+ if (val > 0xffff)
+ return (0);
+ saw_xdigit = 1;
+ continue;
+ }
+ if (ch == ':') {
+ curtok = src;
+ if (!saw_xdigit) {
+ if (colonp)
+ return (0);
+ colonp = tp;
+ continue;
+ }
+ if (tp + NS_INT16SZ > endp)
+ return (0);
+ *tp++ = (unsigned char) (val >> 8) & 0xff;
+ *tp++ = (unsigned char) val & 0xff;
+ saw_xdigit = 0;
+ val = 0;
+ continue;
+ }
+ if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
+ inet_pton4(curtok, tp) > 0) {
+ tp += NS_INADDRSZ;
+ saw_xdigit = 0;
+ break; /* '\0' was seen by inet_pton4(). */
+ }
+ return (0);
+ }
+ if (saw_xdigit) {
+ if (tp + NS_INT16SZ > endp)
+ return (0);
+ *tp++ = (unsigned char) (val >> 8) & 0xff;
+ *tp++ = (unsigned char) val & 0xff;
+ }
+ if (colonp != NULL) {
+ /*
+ * Since some memmove()'s erroneously fail to handle
+ * overlapping regions, we'll do the shift by hand.
+ */
+ const int n = tp - colonp;
+ int i;
+
+ for (i = 1; i <= n; i++) {
+ endp[- i] = colonp[n - i];
+ colonp[n - i] = 0;
+ }
+ tp = endp;
+ }
+ if (tp != endp)
+ return (0);
+ memcpy(dst, tmp, NS_IN6ADDRSZ);
+ return (1);
+}
+#endif
diff --git a/rsync/lib/md5.c b/rsync/lib/md5.c
new file mode 100644
index 0000000..4baa963
--- /dev/null
+++ b/rsync/lib/md5.c
@@ -0,0 +1,304 @@
+/*
+ * RFC 1321 compliant MD5 implementation
+ *
+ * Copyright (C) 2001-2003 Christophe Devine
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+void md5_begin(md_context *ctx)
+{
+ ctx->A = 0x67452301;
+ ctx->B = 0xEFCDAB89;
+ ctx->C = 0x98BADCFE;
+ ctx->D = 0x10325476;
+
+ ctx->totalN = ctx->totalN2 = 0;
+}
+
+static void md5_process(md_context *ctx, const uchar data[CSUM_CHUNK])
+{
+ uint32 X[16], A, B, C, D;
+
+ A = ctx->A;
+ B = ctx->B;
+ C = ctx->C;
+ D = ctx->D;
+
+ X[0] = IVALu(data, 0);
+ X[1] = IVALu(data, 4);
+ X[2] = IVALu(data, 8);
+ X[3] = IVALu(data, 12);
+ X[4] = IVALu(data, 16);
+ X[5] = IVALu(data, 20);
+ X[6] = IVALu(data, 24);
+ X[7] = IVALu(data, 28);
+ X[8] = IVALu(data, 32);
+ X[9] = IVALu(data, 36);
+ X[10] = IVALu(data, 40);
+ X[11] = IVALu(data, 44);
+ X[12] = IVALu(data, 48);
+ X[13] = IVALu(data, 52);
+ X[14] = IVALu(data, 56);
+ X[15] = IVALu(data, 60);
+
+#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
+
+#define P(a,b,c,d,k,s,t) a += F(b,c,d) + X[k] + t, a = S(a,s) + b
+
+#define F(x,y,z) (z ^ (x & (y ^ z)))
+
+ P(A, B, C, D, 0, 7, 0xD76AA478);
+ P(D, A, B, C, 1, 12, 0xE8C7B756);
+ P(C, D, A, B, 2, 17, 0x242070DB);
+ P(B, C, D, A, 3, 22, 0xC1BDCEEE);
+ P(A, B, C, D, 4, 7, 0xF57C0FAF);
+ P(D, A, B, C, 5, 12, 0x4787C62A);
+ P(C, D, A, B, 6, 17, 0xA8304613);
+ P(B, C, D, A, 7, 22, 0xFD469501);
+ P(A, B, C, D, 8, 7, 0x698098D8);
+ P(D, A, B, C, 9, 12, 0x8B44F7AF);
+ P(C, D, A, B, 10, 17, 0xFFFF5BB1);
+ P(B, C, D, A, 11, 22, 0x895CD7BE);
+ P(A, B, C, D, 12, 7, 0x6B901122);
+ P(D, A, B, C, 13, 12, 0xFD987193);
+ P(C, D, A, B, 14, 17, 0xA679438E);
+ P(B, C, D, A, 15, 22, 0x49B40821);
+
+#undef F
+#define F(x,y,z) (y ^ (z & (x ^ y)))
+
+ P(A, B, C, D, 1, 5, 0xF61E2562);
+ P(D, A, B, C, 6, 9, 0xC040B340);
+ P(C, D, A, B, 11, 14, 0x265E5A51);
+ P(B, C, D, A, 0, 20, 0xE9B6C7AA);
+ P(A, B, C, D, 5, 5, 0xD62F105D);
+ P(D, A, B, C, 10, 9, 0x02441453);
+ P(C, D, A, B, 15, 14, 0xD8A1E681);
+ P(B, C, D, A, 4, 20, 0xE7D3FBC8);
+ P(A, B, C, D, 9, 5, 0x21E1CDE6);
+ P(D, A, B, C, 14, 9, 0xC33707D6);
+ P(C, D, A, B, 3, 14, 0xF4D50D87);
+ P(B, C, D, A, 8, 20, 0x455A14ED);
+ P(A, B, C, D, 13, 5, 0xA9E3E905);
+ P(D, A, B, C, 2, 9, 0xFCEFA3F8);
+ P(C, D, A, B, 7, 14, 0x676F02D9);
+ P(B, C, D, A, 12, 20, 0x8D2A4C8A);
+
+#undef F
+#define F(x,y,z) (x ^ y ^ z)
+
+ P(A, B, C, D, 5, 4, 0xFFFA3942);
+ P(D, A, B, C, 8, 11, 0x8771F681);
+ P(C, D, A, B, 11, 16, 0x6D9D6122);
+ P(B, C, D, A, 14, 23, 0xFDE5380C);
+ P(A, B, C, D, 1, 4, 0xA4BEEA44);
+ P(D, A, B, C, 4, 11, 0x4BDECFA9);
+ P(C, D, A, B, 7, 16, 0xF6BB4B60);
+ P(B, C, D, A, 10, 23, 0xBEBFBC70);
+ P(A, B, C, D, 13, 4, 0x289B7EC6);
+ P(D, A, B, C, 0, 11, 0xEAA127FA);
+ P(C, D, A, B, 3, 16, 0xD4EF3085);
+ P(B, C, D, A, 6, 23, 0x04881D05);
+ P(A, B, C, D, 9, 4, 0xD9D4D039);
+ P(D, A, B, C, 12, 11, 0xE6DB99E5);
+ P(C, D, A, B, 15, 16, 0x1FA27CF8);
+ P(B, C, D, A, 2, 23, 0xC4AC5665);
+
+#undef F
+#define F(x,y,z) (y ^ (x | ~z))
+
+ P(A, B, C, D, 0, 6, 0xF4292244);
+ P(D, A, B, C, 7, 10, 0x432AFF97);
+ P(C, D, A, B, 14, 15, 0xAB9423A7);
+ P(B, C, D, A, 5, 21, 0xFC93A039);
+ P(A, B, C, D, 12, 6, 0x655B59C3);
+ P(D, A, B, C, 3, 10, 0x8F0CCC92);
+ P(C, D, A, B, 10, 15, 0xFFEFF47D);
+ P(B, C, D, A, 1, 21, 0x85845DD1);
+ P(A, B, C, D, 8, 6, 0x6FA87E4F);
+ P(D, A, B, C, 15, 10, 0xFE2CE6E0);
+ P(C, D, A, B, 6, 15, 0xA3014314);
+ P(B, C, D, A, 13, 21, 0x4E0811A1);
+ P(A, B, C, D, 4, 6, 0xF7537E82);
+ P(D, A, B, C, 11, 10, 0xBD3AF235);
+ P(C, D, A, B, 2, 15, 0x2AD7D2BB);
+ P(B, C, D, A, 9, 21, 0xEB86D391);
+
+#undef F
+
+ ctx->A += A;
+ ctx->B += B;
+ ctx->C += C;
+ ctx->D += D;
+}
+
+void md5_update(md_context *ctx, const uchar *input, uint32 length)
+{
+ uint32 left, fill;
+
+ if (!length)
+ return;
+
+ left = ctx->totalN & 0x3F;
+ fill = CSUM_CHUNK - left;
+
+ ctx->totalN += length;
+ ctx->totalN &= 0xFFFFFFFF;
+
+ if (ctx->totalN < length)
+ ctx->totalN2++;
+
+ if (left && length >= fill) {
+ memcpy(ctx->buffer + left, input, fill);
+ md5_process(ctx, ctx->buffer);
+ length -= fill;
+ input += fill;
+ left = 0;
+ }
+
+ while (length >= CSUM_CHUNK) {
+ md5_process(ctx, input);
+ length -= CSUM_CHUNK;
+ input += CSUM_CHUNK;
+ }
+
+ if (length)
+ memcpy(ctx->buffer + left, input, length);
+}
+
+static uchar md5_padding[CSUM_CHUNK] = { 0x80 };
+
+void md5_result(md_context *ctx, uchar digest[MD5_DIGEST_LEN])
+{
+ uint32 last, padn;
+ uint32 high, low;
+ uchar msglen[8];
+
+ high = (ctx->totalN >> 29)
+ | (ctx->totalN2 << 3);
+ low = (ctx->totalN << 3);
+
+ SIVALu(msglen, 0, low);
+ SIVALu(msglen, 4, high);
+
+ last = ctx->totalN & 0x3F;
+ padn = last < 56 ? 56 - last : 120 - last;
+
+ md5_update(ctx, md5_padding, padn);
+ md5_update(ctx, msglen, 8);
+
+ SIVALu(digest, 0, ctx->A);
+ SIVALu(digest, 4, ctx->B);
+ SIVALu(digest, 8, ctx->C);
+ SIVALu(digest, 12, ctx->D);
+}
+
+void get_md5(uchar *out, const uchar *input, int n)
+{
+ md_context ctx;
+ md5_begin(&ctx);
+ md5_update(&ctx, input, n);
+ md5_result(&ctx, out);
+}
+
+#ifdef TEST_MD5
+
+#include
+#include
+
+/*
+ * those are the standard RFC 1321 test vectors
+ */
+
+static struct {
+ char *str, *md5;
+} tests[] = {
+ { "",
+ "d41d8cd98f00b204e9800998ecf8427e" },
+ { "a",
+ "0cc175b9c0f1b6a831c399e269772661" },
+ { "abc",
+ "900150983cd24fb0d6963f7d28e17f72" },
+ { "message digest",
+ "f96b697d7cb7938d525a2f31aaf161d0" },
+ { "abcdefghijklmnopqrstuvwxyz",
+ "c3fcd3d76192e4007dfb496cca67e13b" },
+ { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ "d174ab98d277d9f5a5611c2c9f419d9f" },
+ { "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
+ "57edf4a22be3c955ac49da2e2107b67a" },
+ { NULL, NULL }
+};
+
+int main(int argc, char *argv[])
+{
+ FILE *f;
+ int i, j;
+ char output[33];
+ md_context ctx;
+ uchar buf[1000];
+ uchar md5sum[MD5_DIGEST_LEN];
+
+ if (argc < 2) {
+ printf("\nMD5 Validation Tests:\n\n");
+
+ for (i = 0; tests[i].str; i++) {
+ char *str = tests[i].str;
+ char *chk = tests[i].md5;
+
+ printf(" Test %d ", i + 1);
+
+ get_md5(md5sum, str, strlen(str));
+
+ for (j = 0; j < MD5_DIGEST_LEN; j++)
+ sprintf(output + j * 2, "%02x", md5sum[j]);
+
+ if (memcmp(output, chk, 32)) {
+ printf("failed!\n");
+ return 1;
+ }
+
+ printf("passed.\n");
+ }
+
+ printf("\n");
+ return 0;
+ }
+
+ while (--argc) {
+ if (!(f = fopen(*++argv, "rb"))) {
+ perror("fopen");
+ return 1;
+ }
+
+ md5_begin(&ctx);
+
+ while ((i = fread(buf, 1, sizeof buf, f)) > 0)
+ md5_update(&ctx, buf, i);
+
+ md5_result(&ctx, md5sum);
+
+ for (j = 0; j < MD5_DIGEST_LEN; j++)
+ printf("%02x", md5sum[j]);
+
+ printf(" %s\n", *argv);
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/rsync/lib/mdfour.c b/rsync/lib/mdfour.c
new file mode 100644
index 0000000..16b2358
--- /dev/null
+++ b/rsync/lib/mdfour.c
@@ -0,0 +1,246 @@
+/*
+ * Unix SMB/Netbios implementation.
+ * Version 1.9.
+ * An implementation of MD4 designed for use in the SMB authentication protocol.
+ *
+ * Copyright (C) 1997-1998 Andrew Tridgell
+ * Copyright (C) 2005-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+/* NOTE: This code makes no attempt to be fast!
+ *
+ * It assumes that a int is at least 32 bits long. */
+
+static md_context *m;
+
+#define MASK32 (0xffffffff)
+
+#define F(X,Y,Z) ((((X)&(Y)) | ((~(X))&(Z))))
+#define G(X,Y,Z) ((((X)&(Y)) | ((X)&(Z)) | ((Y)&(Z))))
+#define H(X,Y,Z) (((X)^(Y)^(Z)))
+#define lshift(x,s) (((((x)<<(s))&MASK32) | (((x)>>(32-(s)))&MASK32)))
+
+#define ROUND1(a,b,c,d,k,s) a = lshift((a + F(b,c,d) + M[k])&MASK32, s)
+#define ROUND2(a,b,c,d,k,s) a = lshift((a + G(b,c,d) + M[k] + 0x5A827999)&MASK32,s)
+#define ROUND3(a,b,c,d,k,s) a = lshift((a + H(b,c,d) + M[k] + 0x6ED9EBA1)&MASK32,s)
+
+/* this applies md4 to 64 byte chunks */
+static void mdfour64(uint32 *M)
+{
+ uint32 AA, BB, CC, DD;
+ uint32 A,B,C,D;
+
+ A = m->A; B = m->B; C = m->C; D = m->D;
+ AA = A; BB = B; CC = C; DD = D;
+
+ ROUND1(A,B,C,D, 0, 3); ROUND1(D,A,B,C, 1, 7);
+ ROUND1(C,D,A,B, 2, 11); ROUND1(B,C,D,A, 3, 19);
+ ROUND1(A,B,C,D, 4, 3); ROUND1(D,A,B,C, 5, 7);
+ ROUND1(C,D,A,B, 6, 11); ROUND1(B,C,D,A, 7, 19);
+ ROUND1(A,B,C,D, 8, 3); ROUND1(D,A,B,C, 9, 7);
+ ROUND1(C,D,A,B, 10, 11); ROUND1(B,C,D,A, 11, 19);
+ ROUND1(A,B,C,D, 12, 3); ROUND1(D,A,B,C, 13, 7);
+ ROUND1(C,D,A,B, 14, 11); ROUND1(B,C,D,A, 15, 19);
+
+ ROUND2(A,B,C,D, 0, 3); ROUND2(D,A,B,C, 4, 5);
+ ROUND2(C,D,A,B, 8, 9); ROUND2(B,C,D,A, 12, 13);
+ ROUND2(A,B,C,D, 1, 3); ROUND2(D,A,B,C, 5, 5);
+ ROUND2(C,D,A,B, 9, 9); ROUND2(B,C,D,A, 13, 13);
+ ROUND2(A,B,C,D, 2, 3); ROUND2(D,A,B,C, 6, 5);
+ ROUND2(C,D,A,B, 10, 9); ROUND2(B,C,D,A, 14, 13);
+ ROUND2(A,B,C,D, 3, 3); ROUND2(D,A,B,C, 7, 5);
+ ROUND2(C,D,A,B, 11, 9); ROUND2(B,C,D,A, 15, 13);
+
+ ROUND3(A,B,C,D, 0, 3); ROUND3(D,A,B,C, 8, 9);
+ ROUND3(C,D,A,B, 4, 11); ROUND3(B,C,D,A, 12, 15);
+ ROUND3(A,B,C,D, 2, 3); ROUND3(D,A,B,C, 10, 9);
+ ROUND3(C,D,A,B, 6, 11); ROUND3(B,C,D,A, 14, 15);
+ ROUND3(A,B,C,D, 1, 3); ROUND3(D,A,B,C, 9, 9);
+ ROUND3(C,D,A,B, 5, 11); ROUND3(B,C,D,A, 13, 15);
+ ROUND3(A,B,C,D, 3, 3); ROUND3(D,A,B,C, 11, 9);
+ ROUND3(C,D,A,B, 7, 11); ROUND3(B,C,D,A, 15, 15);
+
+ A += AA; B += BB;
+ C += CC; D += DD;
+
+ A &= MASK32; B &= MASK32;
+ C &= MASK32; D &= MASK32;
+
+ m->A = A; m->B = B; m->C = C; m->D = D;
+}
+
+static void copy64(uint32 *M, const uchar *in)
+{
+ int i;
+
+ for (i = 0; i < MD4_DIGEST_LEN; i++) {
+ M[i] = (in[i*4+3] << 24) | (in[i*4+2] << 16)
+ | (in[i*4+1] << 8) | (in[i*4+0] << 0);
+ }
+}
+
+static void copy4(uchar *out,uint32 x)
+{
+ out[0] = x&0xFF;
+ out[1] = (x>>8)&0xFF;
+ out[2] = (x>>16)&0xFF;
+ out[3] = (x>>24)&0xFF;
+}
+
+void mdfour_begin(md_context *md)
+{
+ md->A = 0x67452301;
+ md->B = 0xefcdab89;
+ md->C = 0x98badcfe;
+ md->D = 0x10325476;
+ md->totalN = 0;
+ md->totalN2 = 0;
+}
+
+static void mdfour_tail(const uchar *in, uint32 length)
+{
+ uchar buf[128];
+ uint32 M[16];
+ extern int protocol_version;
+
+ /*
+ * Count total number of bits, modulo 2^64
+ */
+ m->totalN += length << 3;
+ if (m->totalN < (length << 3))
+ m->totalN2++;
+ m->totalN2 += length >> 29;
+
+ memset(buf, 0, 128);
+ if (length)
+ memcpy(buf, in, length);
+ buf[length] = 0x80;
+
+ if (length <= 55) {
+ copy4(buf+56, m->totalN);
+ /*
+ * Prior to protocol version 27 only the number of bits
+ * modulo 2^32 was included. MD4 requires the number
+ * of bits modulo 2^64, which was fixed starting with
+ * protocol version 27.
+ */
+ if (protocol_version >= 27)
+ copy4(buf+60, m->totalN2);
+ copy64(M, buf);
+ mdfour64(M);
+ } else {
+ copy4(buf+120, m->totalN);
+ /*
+ * Prior to protocol version 27 only the number of bits
+ * modulo 2^32 was included. MD4 requires the number
+ * of bits modulo 2^64, which was fixed starting with
+ * protocol version 27.
+ */
+ if (protocol_version >= 27)
+ copy4(buf+124, m->totalN2);
+ copy64(M, buf);
+ mdfour64(M);
+ copy64(M, buf+64);
+ mdfour64(M);
+ }
+}
+
+void mdfour_update(md_context *md, const uchar *in, uint32 length)
+{
+ uint32 M[16];
+
+ m = md;
+
+ if (length == 0)
+ mdfour_tail(in, length);
+
+ while (length >= 64) {
+ copy64(M, in);
+ mdfour64(M);
+ in += 64;
+ length -= 64;
+ m->totalN += 64 << 3;
+ if (m->totalN < 64 << 3)
+ m->totalN2++;
+ }
+
+ if (length)
+ mdfour_tail(in, length);
+}
+
+void mdfour_result(md_context *md, uchar digest[MD4_DIGEST_LEN])
+{
+ m = md;
+
+ copy4(digest, m->A);
+ copy4(digest+4, m->B);
+ copy4(digest+8, m->C);
+ copy4(digest+12, m->D);
+}
+
+void mdfour(uchar digest[MD4_DIGEST_LEN], uchar *in, int length)
+{
+ md_context md;
+ mdfour_begin(&md);
+ mdfour_update(&md, in, length);
+ mdfour_result(&md, digest);
+}
+
+#ifdef TEST_MDFOUR
+int protocol_version = 28;
+
+static void file_checksum1(char *fname)
+{
+ int fd, i, was_multiple_of_64 = 1;
+ md_context md;
+ uchar buf[64*1024], sum[MD4_DIGEST_LEN];
+
+ fd = open(fname,O_RDONLY);
+ if (fd == -1) {
+ perror("fname");
+ exit(1);
+ }
+
+ mdfour_begin(&md);
+
+ while (1) {
+ int n = read(fd, buf, sizeof buf);
+ if (n <= 0)
+ break;
+ was_multiple_of_64 = !(n % 64);
+ mdfour_update(&md, buf, n);
+ }
+ if (was_multiple_of_64 && protocol_version >= 27)
+ mdfour_update(&md, buf, 0);
+
+ close(fd);
+
+ mdfour_result(&md, sum);
+
+ for (i = 0; i < MD4_DIGEST_LEN; i++)
+ printf("%02X", sum[i]);
+ printf("\n");
+}
+
+ int main(int argc, char *argv[])
+{
+ while (--argc)
+ file_checksum1(*++argv);
+ return 0;
+}
+#endif
diff --git a/rsync/lib/mdigest.h b/rsync/lib/mdigest.h
new file mode 100644
index 0000000..e0e33ed
--- /dev/null
+++ b/rsync/lib/mdigest.h
@@ -0,0 +1,26 @@
+/* The include file for both the MD4 and MD5 routines. */
+
+#define MD4_DIGEST_LEN 16
+#define MD5_DIGEST_LEN 16
+#define MAX_DIGEST_LEN MD5_DIGEST_LEN
+
+#define CSUM_CHUNK 64
+
+typedef struct {
+ uint32 A, B, C, D;
+ uint32 totalN; /* bit count, lower 32 bits */
+ uint32 totalN2; /* bit count, upper 32 bits */
+ uchar buffer[CSUM_CHUNK];
+} md_context;
+
+void mdfour_begin(md_context *md);
+void mdfour_update(md_context *md, const uchar *in, uint32 length);
+void mdfour_result(md_context *md, uchar digest[MD4_DIGEST_LEN]);
+
+void get_mdfour(uchar digest[MD4_DIGEST_LEN], const uchar *in, int length);
+
+void md5_begin(md_context *ctx);
+void md5_update(md_context *ctx, const uchar *input, uint32 length);
+void md5_result(md_context *ctx, uchar digest[MD5_DIGEST_LEN]);
+
+void get_md5(uchar digest[MD5_DIGEST_LEN], const uchar *input, int n);
diff --git a/rsync/lib/permstring.c b/rsync/lib/permstring.c
new file mode 100644
index 0000000..7b2414d
--- /dev/null
+++ b/rsync/lib/permstring.c
@@ -0,0 +1,65 @@
+/*
+ * A single utility routine.
+ *
+ * Copyright (C) 1996 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2001 Martin Pool
+ * Copyright (C) 2003-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+/* Produce a string representation of Unix mode bits like that used by ls(1).
+ * The "buf" buffer must be at least 11 characters. */
+void permstring(char *perms, mode_t mode)
+{
+ static const char *perm_map = "rwxrwxrwx";
+ int i;
+
+ strlcpy(perms, "----------", 11);
+
+ for (i = 0; i < 9; i++) {
+ if (mode & (1 << i))
+ perms[9-i] = perm_map[8-i];
+ }
+
+ /* Handle setuid/sticky bits. You might think the indices are
+ * off by one, but remember there's a type char at the
+ * start. */
+ if (mode & S_ISUID)
+ perms[3] = (mode & S_IXUSR) ? 's' : 'S';
+
+ if (mode & S_ISGID)
+ perms[6] = (mode & S_IXGRP) ? 's' : 'S';
+
+#ifdef S_ISVTX
+ if (mode & S_ISVTX)
+ perms[9] = (mode & S_IXOTH) ? 't' : 'T';
+#endif
+
+ if (S_ISDIR(mode))
+ perms[0] = 'd';
+ else if (S_ISLNK(mode))
+ perms[0] = 'l';
+ else if (S_ISBLK(mode))
+ perms[0] = 'b';
+ else if (S_ISCHR(mode))
+ perms[0] = 'c';
+ else if (S_ISSOCK(mode))
+ perms[0] = 's';
+ else if (S_ISFIFO(mode))
+ perms[0] = 'p';
+}
diff --git a/rsync/lib/permstring.h b/rsync/lib/permstring.h
new file mode 100644
index 0000000..b996f2a
--- /dev/null
+++ b/rsync/lib/permstring.h
@@ -0,0 +1,3 @@
+#define PERMSTRING_SIZE 11
+
+void permstring(char *perms, mode_t mode);
diff --git a/rsync/lib/pool_alloc.3 b/rsync/lib/pool_alloc.3
new file mode 100644
index 0000000..6c22b92
--- /dev/null
+++ b/rsync/lib/pool_alloc.3
@@ -0,0 +1,268 @@
+.ds d \-\^\-
+.ds o \fR[\fP
+.ds c \fR]\fP
+.ds | \fR|\fP
+.de D
+\\.B \*d\\$1
+..
+.de DI
+\\.BI \*d\\$1 \\$2
+..
+.de DR
+\\.BR \*d\\$1 \\$2
+..
+.de Di
+\\.BI \*d\\$1 " \\$2"
+..
+.de Db
+\\.B \*d\\$1 " \\$2"
+..
+.de Df
+\\.B \*d\*ono\*c\\$1
+..
+.de See
+See \fB\\$1\fP for details.
+..
+.de SeeIn
+See \fB\\$1\fP in \fB\\$2\fP for details.
+..
+.TH POOL_ALLOC 3
+.SH NAME
+pool_alloc, pool_free, pool_free_old, pool_talloc, pool_tfree, pool_create, pool_destroy, pool_boundary
+\- Allocate and free memory in managed allocation pools.
+.SH SYNOPSIS
+.B #include "pool_alloc.h"
+
+\fBstruct alloc_pool *pool_create(size_t \fIsize\fB, size_t \fIquantum\fB, void (*\fIbomb\fB)(char *), int \fIflags\fB);
+
+\fBvoid pool_destroy(struct alloc_pool *\fIpool\fB);
+
+\fBvoid *pool_alloc(struct alloc_pool *\fIpool\fB, size_t \fIsize\fB, char *\fImsg\fB);
+
+\fBvoid pool_free(struct alloc_pool *\fIpool\fB, size_t \fIsize\fB, void *\fIaddr\fB);
+
+\fBvoid pool_free_old(struct alloc_pool *\fIpool\fB, void *\fIaddr\fB);
+
+\fBvoid *pool_talloc(struct alloc_pool *\fIpool\fB, \fItype\fB), int \fIcount\fB, char *\fImsg\fB);
+
+\fBvoid pool_tfree(struct alloc_pool *\fIpool\fB, \fItype\fB, int \fIcount\fB, void *\fIaddr\fB);
+
+\fBvoid pool_boundary(struct alloc_pool *\fIpool\fB, sise_t \fIsize\fB);
+.SH DESCRIPTION
+.P
+The pool allocation routines use
+.B malloc()
+for underlying memory management.
+What allocation pools do is cause memory within a given pool
+to be allocated in large contiguous blocks
+(called extents) that will be reusable when freed. Unlike
+.BR malloc() ,
+the allocations are not managed individually.
+Instead, each extent tracks the total free memory within the
+extent. Each extent can either be used to allocate memory
+or to manage the freeing of memory within that extent.
+When an extent has less free memory than a given
+allocation request, the current extent ceases to be used
+for allocation. See also the
+.B pool_boundary()
+function.
+.P
+This form of memory management is suited to large numbers of small
+related allocations that are held for a while
+and then freed as a group.
+Because the
+underlying allocations are done in large contiguous extents,
+when an extent is freed, it can release a large enough
+contiguous block of memory to allow the memory to be returned
+to the OS for use by whatever program needs it.
+You can allocate from one or more memory pools and/or
+.B malloc()
+all at the same time without interfering with how pools work.
+.P
+.B pool_create()
+Creates an allocation pool for subsequent calls to the pool
+allocation functions.
+When an extent is created for allocations it will be
+.I size
+bytes.
+Allocations from the pool have their sizes rounded up to a
+multiple of
+.I quantum
+bytes in length.
+Specifying
+.B 0
+for
+.I quantum
+will produce a quantum that should meet maximal alignment
+on most platforms.
+Unless
+.B POOL_NO_QALIGN
+is set in the
+.IR flags ,
+allocations will be aligned to addresses that are a
+multiple of
+.IR quantum .
+A
+.B NULL
+may be specified for the
+.I bomb
+function pointer if it is not needed. (See the
+.B pool_alloc()
+function for how it is used.)
+If
+.B POOL_CLEAR
+is set in the
+.IR flags ,
+all allocations from the pool will be initialized to zeros.
+If either
+.B POOL_PREPEND
+or
+.B POOL_INTERN
+is specified in the
+.IR flags ,
+each extent's data structure will be allocated at the start of the
+.IR size -length
+buffer (rather than as a separate, non-pool allocation), with the
+former extending the
+.I size
+to hold the structure, and the latter subtracting the structure's
+length from the indicated
+.IR size .
+.P
+.B pool_destroy()
+destroys an allocation
+.I pool
+and frees all its associated memory.
+.P
+.B pool_alloc()
+allocates
+.I size
+bytes from the specified
+.IR pool .
+If
+.I size
+is
+.BR 0 ,
+.I quantum
+bytes will be allocated.
+If the pool has been created without
+.BR POOL_NO_QALIGN ,
+every chunk of memory that is returned will be suitably aligned.
+You can use this with the default
+.I quantum
+size to ensure that all memory can store a variable of any type.
+If the requested memory cannot be allocated, the
+.I bomb()
+function will be called with
+.I msg
+as its sole argument (if the function was defined at the time
+the pool was created), and then a
+.B NULL
+address is returned (assuming that the bomb function didn't exit).
+.P
+.B pool_free()
+frees
+.I size
+bytes pointed to by an
+.I addr
+that was previously allocated in the specified
+.IR pool .
+If
+.I size
+is
+.BR 0 ,
+.I quantum
+bytes will be freed.
+The memory freed within an extent will not be reusable until
+all of the memory in that extent has been freed with one
+exception: the most recent pool allocation may be freed back
+into the pool prior to making any further allocations.
+If enough free calls are made to indicate that an extent has no
+remaining allocated objects (as computed by the total freed size for
+an extent), its memory will be completely freed back to the system.
+If
+.I addr
+is
+.BR NULL ,
+no memory will be freed, but subsequent allocations will come
+from a new extent.
+.P
+.B pool_free_old()
+takes a boundary
+.I addr
+value that was returned by
+.B pool_boundary()
+and frees up any extents in the
+.I pool
+that have data allocated from that point backward in time.
+NOTE: you must NOT mix calls to both
+.B pool_free
+and
+.B pool_free_old
+on the same pool!
+.P
+.B pool_boundary()
+asks for a boundary value that can be sent to
+.B pool_free_old()
+at a later time to free up all memory allocated prior to a particular
+moment in time.
+If the extent that holds the boundary point has allocations from after the
+boundary point, it will not be freed until a future
+.B pool_free_old()
+call encompasses the entirety of the extent's data.
+If
+.I len
+is non-zero, the call will also check if the active extent has at least
+that much free memory available in it, and if not, it will mark the
+extent as inactive, forcing a new extent to be used for future allocations.
+(You can specify -1 for
+.I len
+if you want to force a new extent to start.)
+.P
+.B pool_talloc()
+is a macro that takes a
+.I type
+and a
+.I count
+instead of a
+.IR size .
+It casts the return value to the correct pointer type.
+.P
+.B pool_tfree
+is a macro that calls
+.B pool_free
+on memory that was allocated by
+.BR pool_talloc() .
+.SH RETURN VALUE
+.B pool_create()
+returns a pointer to
+.BR "struct alloc_pool" .
+.P
+.B pool_alloc()
+and
+.B pool_talloc()
+return pointers to the allocated memory,
+or NULL if the request fails.
+The return type of
+.B pool_alloc()
+will normally require casting to the desired type but
+.B pool_talloc()
+will returns a pointer of the requested
+.IR type .
+.P
+.B pool_boundary()
+returns a pointer that should only be used in a call to
+.BR pool_free_old() .
+.P
+.BR pool_free() ,
+.BR pool_free_old() ,
+.B pool_tfree()
+and
+.B pool_destroy()
+return no value.
+.SH SEE ALSO
+.nf
+malloc(3)
+.SH AUTHOR
+pool_alloc was created by J.W. Schultz of Pegasystems Technologies.
+.SH BUGS AND ISSUES
diff --git a/rsync/lib/pool_alloc.c b/rsync/lib/pool_alloc.c
new file mode 100644
index 0000000..5856d59
--- /dev/null
+++ b/rsync/lib/pool_alloc.c
@@ -0,0 +1,376 @@
+#include "rsync.h"
+
+#define POOL_DEF_EXTENT (32 * 1024)
+
+#define POOL_QALIGN_P2 (1<<16) /* power-of-2 qalign */
+
+struct alloc_pool
+{
+ size_t size; /* extent size */
+ size_t quantum; /* allocation quantum */
+ struct pool_extent *extents; /* top extent is "live" */
+ void (*bomb)(); /* function to call if
+ * malloc fails */
+ int flags;
+
+ /* statistical data */
+ unsigned long e_created; /* extents created */
+ unsigned long e_freed; /* extents destroyed */
+ int64 n_allocated; /* calls to alloc */
+ int64 n_freed; /* calls to free */
+ int64 b_allocated; /* cum. bytes allocated */
+ int64 b_freed; /* cum. bytes freed */
+};
+
+struct pool_extent
+{
+ struct pool_extent *next;
+ void *start; /* starting address */
+ size_t free; /* free bytecount */
+ size_t bound; /* trapped free bytes */
+};
+
+struct align_test {
+ uchar foo;
+ union {
+ int64 i;
+ void *p;
+ } bar;
+};
+
+#define MINALIGN offsetof(struct align_test, bar)
+
+/* Temporarily cast a void* var into a char* var when adding an offset (to
+ * keep some compilers from complaining about the pointer arithmetic). */
+#define PTR_ADD(b,o) ( (void*) ((char*)(b) + (o)) )
+
+alloc_pool_t
+pool_create(size_t size, size_t quantum, void (*bomb)(const char *), int flags)
+{
+ struct alloc_pool *pool;
+
+ if (!(pool = new0(struct alloc_pool)))
+ return NULL;
+
+ if ((MINALIGN & (MINALIGN - 1)) != 0) {
+ if (bomb)
+ (*bomb)("Compiler error: MINALIGN is not a power of 2\n");
+ return NULL;
+ }
+
+ if (!size)
+ size = POOL_DEF_EXTENT;
+ if (!quantum)
+ quantum = MINALIGN;
+
+ if (flags & POOL_INTERN) {
+ if (size <= sizeof (struct pool_extent))
+ size = quantum;
+ else
+ size -= sizeof (struct pool_extent);
+ flags |= POOL_PREPEND;
+ }
+
+ if (quantum <= 1)
+ flags = (flags | POOL_NO_QALIGN) & ~POOL_QALIGN_P2;
+ else if (!(flags & POOL_NO_QALIGN)) {
+ if (size % quantum)
+ size += quantum - size % quantum;
+ /* If quantum is a power of 2, we'll avoid using modulus. */
+ if (!(quantum & (quantum - 1)))
+ flags |= POOL_QALIGN_P2;
+ }
+
+ pool->size = size;
+ pool->quantum = quantum;
+ pool->bomb = bomb;
+ pool->flags = flags;
+
+ return pool;
+}
+
+void
+pool_destroy(alloc_pool_t p)
+{
+ struct alloc_pool *pool = (struct alloc_pool *) p;
+ struct pool_extent *cur, *next;
+
+ if (!pool)
+ return;
+
+ for (cur = pool->extents; cur; cur = next) {
+ next = cur->next;
+ if (pool->flags & POOL_PREPEND)
+ free(PTR_ADD(cur->start, -sizeof (struct pool_extent)));
+ else {
+ free(cur->start);
+ free(cur);
+ }
+ }
+
+ free(pool);
+}
+
+void *
+pool_alloc(alloc_pool_t p, size_t len, const char *bomb_msg)
+{
+ struct alloc_pool *pool = (struct alloc_pool *) p;
+ if (!pool)
+ return NULL;
+
+ if (!len)
+ len = pool->quantum;
+ else if (pool->flags & POOL_QALIGN_P2) {
+ if (len & (pool->quantum - 1))
+ len += pool->quantum - (len & (pool->quantum - 1));
+ } else if (!(pool->flags & POOL_NO_QALIGN)) {
+ if (len % pool->quantum)
+ len += pool->quantum - len % pool->quantum;
+ }
+
+ if (len > pool->size)
+ goto bomb_out;
+
+ if (!pool->extents || len > pool->extents->free) {
+ void *start;
+ size_t asize;
+ struct pool_extent *ext;
+
+ asize = pool->size;
+ if (pool->flags & POOL_PREPEND)
+ asize += sizeof (struct pool_extent);
+
+ if (!(start = new_array(char, asize)))
+ goto bomb_out;
+
+ if (pool->flags & POOL_CLEAR)
+ memset(start, 0, asize);
+
+ if (pool->flags & POOL_PREPEND) {
+ ext = start;
+ start = PTR_ADD(start, sizeof (struct pool_extent));
+ } else if (!(ext = new(struct pool_extent)))
+ goto bomb_out;
+ ext->start = start;
+ ext->free = pool->size;
+ ext->bound = 0;
+ ext->next = pool->extents;
+ pool->extents = ext;
+
+ pool->e_created++;
+ }
+
+ pool->n_allocated++;
+ pool->b_allocated += len;
+
+ pool->extents->free -= len;
+
+ return PTR_ADD(pool->extents->start, pool->extents->free);
+
+ bomb_out:
+ if (pool->bomb)
+ (*pool->bomb)(bomb_msg);
+ return NULL;
+}
+
+/* This function allows you to declare memory in the pool that you are done
+ * using. If you free all the memory in a pool's extent, that extent will
+ * be freed. */
+void
+pool_free(alloc_pool_t p, size_t len, void *addr)
+{
+ struct alloc_pool *pool = (struct alloc_pool *)p;
+ struct pool_extent *cur, *prev;
+
+ if (!pool)
+ return;
+
+ if (!addr) {
+ /* A NULL addr starts a fresh extent for new allocations. */
+ if ((cur = pool->extents) != NULL && cur->free != pool->size) {
+ cur->bound += cur->free;
+ cur->free = 0;
+ }
+ return;
+ }
+
+ if (!len)
+ len = pool->quantum;
+ else if (pool->flags & POOL_QALIGN_P2) {
+ if (len & (pool->quantum - 1))
+ len += pool->quantum - (len & (pool->quantum - 1));
+ } else if (!(pool->flags & POOL_NO_QALIGN)) {
+ if (len % pool->quantum)
+ len += pool->quantum - len % pool->quantum;
+ }
+
+ pool->n_freed++;
+ pool->b_freed += len;
+
+ for (prev = NULL, cur = pool->extents; cur; prev = cur, cur = cur->next) {
+ if (addr >= cur->start
+ && addr < PTR_ADD(cur->start, pool->size))
+ break;
+ }
+ if (!cur)
+ return;
+
+ if (!prev) {
+ /* The "live" extent is kept ready for more allocations. */
+ if (cur->free + cur->bound + len >= pool->size) {
+ if (pool->flags & POOL_CLEAR) {
+ memset(PTR_ADD(cur->start, cur->free), 0,
+ pool->size - cur->free);
+ }
+ cur->free = pool->size;
+ cur->bound = 0;
+ } else if (addr == PTR_ADD(cur->start, cur->free)) {
+ if (pool->flags & POOL_CLEAR)
+ memset(addr, 0, len);
+ cur->free += len;
+ } else
+ cur->bound += len;
+ } else {
+ cur->bound += len;
+
+ if (cur->free + cur->bound >= pool->size) {
+ prev->next = cur->next;
+ if (pool->flags & POOL_PREPEND)
+ free(PTR_ADD(cur->start, -sizeof (struct pool_extent)));
+ else {
+ free(cur->start);
+ free(cur);
+ }
+ pool->e_freed++;
+ } else if (prev != pool->extents) {
+ /* Move the extent to be the first non-live extent. */
+ prev->next = cur->next;
+ cur->next = pool->extents->next;
+ pool->extents->next = cur;
+ }
+ }
+}
+
+/* This allows you to declare that the given address marks the edge of some
+ * pool memory that is no longer needed. Any extents that hold only data
+ * older than the boundary address are freed. NOTE: You MUST NOT USE BOTH
+ * pool_free() and pool_free_old() on the same pool!! */
+void
+pool_free_old(alloc_pool_t p, void *addr)
+{
+ struct alloc_pool *pool = (struct alloc_pool *)p;
+ struct pool_extent *cur, *prev, *next;
+
+ if (!pool || !addr)
+ return;
+
+ for (prev = NULL, cur = pool->extents; cur; prev = cur, cur = cur->next) {
+ if (addr >= cur->start
+ && addr < PTR_ADD(cur->start, pool->size))
+ break;
+ }
+ if (!cur)
+ return;
+
+ if (addr == PTR_ADD(cur->start, cur->free)) {
+ if (prev) {
+ prev->next = NULL;
+ next = cur;
+ } else {
+ /* The most recent live extent can just be reset. */
+ if (pool->flags & POOL_CLEAR)
+ memset(addr, 0, pool->size - cur->free);
+ cur->free = pool->size;
+ cur->bound = 0;
+ next = cur->next;
+ cur->next = NULL;
+ }
+ } else {
+ next = cur->next;
+ cur->next = NULL;
+ }
+
+ while ((cur = next) != NULL) {
+ next = cur->next;
+ if (pool->flags & POOL_PREPEND)
+ free(PTR_ADD(cur->start, -sizeof (struct pool_extent)));
+ else {
+ free(cur->start);
+ free(cur);
+ }
+ pool->e_freed++;
+ }
+}
+
+/* If the current extent doesn't have "len" free space in it, mark it as full
+ * so that the next alloc will start a new extent. If len is (size_t)-1, this
+ * bump will always occur. The function returns a boundary address that can
+ * be used with pool_free_old(), or a NULL if no memory is allocated. */
+void *
+pool_boundary(alloc_pool_t p, size_t len)
+{
+ struct alloc_pool *pool = (struct alloc_pool *)p;
+ struct pool_extent *cur;
+
+ if (!pool || !pool->extents)
+ return NULL;
+
+ cur = pool->extents;
+
+ if (cur->free < len) {
+ cur->bound += cur->free;
+ cur->free = 0;
+ }
+
+ return PTR_ADD(cur->start, cur->free);
+}
+
+#define FDPRINT(label, value) \
+ do { \
+ int len = snprintf(buf, sizeof buf, label, value); \
+ if (write(fd, buf, len) != len) \
+ ret = -1; \
+ } while (0)
+
+#define FDEXTSTAT(ext) \
+ do { \
+ int len = snprintf(buf, sizeof buf, " %12ld %5ld\n", \
+ (long)ext->free, (long)ext->bound); \
+ if (write(fd, buf, len) != len) \
+ ret = -1; \
+ } while (0)
+
+int
+pool_stats(alloc_pool_t p, int fd, int summarize)
+{
+ struct alloc_pool *pool = (struct alloc_pool *) p;
+ struct pool_extent *cur;
+ char buf[BUFSIZ];
+ int ret = 0;
+
+ if (!pool)
+ return ret;
+
+ FDPRINT(" Extent size: %12ld\n", (long) pool->size);
+ FDPRINT(" Alloc quantum: %12ld\n", (long) pool->quantum);
+ FDPRINT(" Extents created: %12ld\n", pool->e_created);
+ FDPRINT(" Extents freed: %12ld\n", pool->e_freed);
+ FDPRINT(" Alloc count: %12.0f\n", (double) pool->n_allocated);
+ FDPRINT(" Free Count: %12.0f\n", (double) pool->n_freed);
+ FDPRINT(" Bytes allocated: %12.0f\n", (double) pool->b_allocated);
+ FDPRINT(" Bytes freed: %12.0f\n", (double) pool->b_freed);
+
+ if (summarize)
+ return ret;
+
+ if (!pool->extents)
+ return ret;
+
+ if (write(fd, "\n", 1) != 1)
+ ret = -1;
+
+ for (cur = pool->extents; cur; cur = cur->next)
+ FDEXTSTAT(cur);
+
+ return ret;
+}
diff --git a/rsync/lib/pool_alloc.h b/rsync/lib/pool_alloc.h
new file mode 100644
index 0000000..c7368a7
--- /dev/null
+++ b/rsync/lib/pool_alloc.h
@@ -0,0 +1,21 @@
+#include
+
+#define POOL_CLEAR (1<<0) /* zero fill allocations */
+#define POOL_NO_QALIGN (1<<1) /* don't align data to quanta */
+#define POOL_INTERN (1<<2) /* Allocate extent structures */
+#define POOL_PREPEND (1<<3) /* or prepend to extent data */
+
+typedef void *alloc_pool_t;
+
+alloc_pool_t pool_create(size_t size, size_t quantum, void (*bomb)(const char *), int flags);
+void pool_destroy(alloc_pool_t pool);
+void *pool_alloc(alloc_pool_t pool, size_t size, const char *bomb_msg);
+void pool_free(alloc_pool_t pool, size_t size, void *addr);
+void pool_free_old(alloc_pool_t pool, void *addr);
+void *pool_boundary(alloc_pool_t pool, size_t size);
+
+#define pool_talloc(pool, type, count, bomb_msg) \
+ ((type *)pool_alloc(pool, sizeof(type) * count, bomb_msg))
+
+#define pool_tfree(pool, type, count, addr) \
+ (pool_free(pool, sizeof(type) * count, addr))
diff --git a/rsync/lib/snprintf.c b/rsync/lib/snprintf.c
new file mode 100644
index 0000000..c17868f
--- /dev/null
+++ b/rsync/lib/snprintf.c
@@ -0,0 +1,1512 @@
+/*
+ * NOTE: If you change this file, please merge it into rsync, samba, etc.
+ */
+
+/*
+ * Copyright Patrick Powell 1995
+ * This code is based on code written by Patrick Powell (papowell@astart.com)
+ * It may be used for any purpose as long as this notice remains intact
+ * on all source code distributions
+ */
+
+/**************************************************************
+ * Original:
+ * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
+ * A bombproof version of doprnt (dopr) included.
+ * Sigh. This sort of thing is always nasty do deal with. Note that
+ * the version here does not include floating point...
+ *
+ * snprintf() is used instead of sprintf() as it does limit checks
+ * for string length. This covers a nasty loophole.
+ *
+ * The other functions are there to prevent NULL pointers from
+ * causing nast effects.
+ *
+ * More Recently:
+ * Brandon Long 9/15/96 for mutt 0.43
+ * This was ugly. It is still ugly. I opted out of floating point
+ * numbers, but the formatter understands just about everything
+ * from the normal C string format, at least as far as I can tell from
+ * the Solaris 2.5 printf(3S) man page.
+ *
+ * Brandon Long 10/22/97 for mutt 0.87.1
+ * Ok, added some minimal floating point support, which means this
+ * probably requires libm on most operating systems. Don't yet
+ * support the exponent (e,E) and sigfig (g,G). Also, fmtint()
+ * was pretty badly broken, it just wasn't being exercised in ways
+ * which showed it, so that's been fixed. Also, formated the code
+ * to mutt conventions, and removed dead code left over from the
+ * original. Also, there is now a builtin-test, just compile with:
+ * gcc -I.. -DTEST_SNPRINTF -o snprintf snprintf.c -lm
+ * and run snprintf for results.
+ *
+ * Thomas Roessler 01/27/98 for mutt 0.89i
+ * The PGP code was using unsigned hexadecimal formats.
+ * Unfortunately, unsigned formats simply didn't work.
+ *
+ * Michael Elkins 03/05/98 for mutt 0.90.8
+ * The original code assumed that both snprintf() and vsnprintf() were
+ * missing. Some systems only have snprintf() but not vsnprintf(), so
+ * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
+ *
+ * Andrew Tridgell (tridge@samba.org) Oct 1998
+ * fixed handling of %.0f
+ * added test for HAVE_LONG_DOUBLE
+ *
+ * tridge@samba.org, idra@samba.org, April 2001
+ * got rid of fcvt code (twas buggy and made testing harder)
+ * added C99 semantics
+ *
+ * date: 2002/12/19 19:56:31; author: herb; state: Exp; lines: +2 -0
+ * actually print args for %g and %e
+ *
+ * date: 2002/06/03 13:37:52; author: jmcd; state: Exp; lines: +8 -0
+ * Since includes.h isn't included here, VA_COPY has to be defined here. I don't
+ * see any include file that is guaranteed to be here, so I'm defining it
+ * locally. Fixes AIX and Solaris builds.
+ *
+ * date: 2002/06/03 03:07:24; author: tridge; state: Exp; lines: +5 -13
+ * put the ifdef for HAVE_VA_COPY in one place rather than in lots of
+ * functions
+ *
+ * date: 2002/05/17 14:51:22; author: jmcd; state: Exp; lines: +21 -4
+ * Fix usage of va_list passed as an arg. Use __va_copy before using it
+ * when it exists.
+ *
+ * date: 2002/04/16 22:38:04; author: idra; state: Exp; lines: +20 -14
+ * Fix incorrect zpadlen handling in fmtfp.
+ * Thanks to Ollie Oldham for spotting it.
+ * few mods to make it easier to compile the tests.
+ * addedd the "Ollie" test to the floating point ones.
+ *
+ * Martin Pool (mbp@samba.org) April 2003
+ * Remove NO_CONFIG_H so that the test case can be built within a source
+ * tree with less trouble.
+ * Remove unnecessary SAFE_FREE() definition.
+ *
+ * Martin Pool (mbp@samba.org) May 2003
+ * Put in a prototype for dummy_snprintf() to quiet compiler warnings.
+ *
+ * Move #endif to make sure VA_COPY, LDOUBLE, etc are defined even
+ * if the C library has some snprintf functions already.
+ *
+ * Darren Tucker (dtucker@zip.com.au) 2005
+ * Fix bug allowing read overruns of the source string with "%.*s"
+ * Usually harmless unless the read runs outside the process' allocation
+ * (eg if your malloc does guard pages) in which case it will segfault.
+ * From OpenSSH. Also added test for same.
+ *
+ * Simo Sorce (idra@samba.org) Jan 2006
+ *
+ * Add support for position independent parameters
+ * fix fmtstr now it conforms to sprintf wrt min.max
+ *
+ **************************************************************/
+
+#include "config.h"
+
+#ifdef TEST_SNPRINTF /* need math library headers for testing */
+
+/* In test mode, we pretend that this system doesn't have any snprintf
+ * functions, regardless of what config.h says. */
+# undef HAVE_SNPRINTF
+# undef HAVE_VSNPRINTF
+# undef HAVE_C99_VSNPRINTF
+# undef HAVE_ASPRINTF
+# undef HAVE_VASPRINTF
+# include
+#endif /* TEST_SNPRINTF */
+
+#ifdef HAVE_STRING_H
+#include
+#endif
+
+#ifdef HAVE_STRINGS_H
+#include
+#endif
+#ifdef HAVE_CTYPE_H
+#include
+#endif
+#include
+#include
+#ifdef HAVE_STDLIB_H
+#include
+#endif
+
+#if defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRINTF)
+/* only include stdio.h if we are not re-defining snprintf or vsnprintf */
+#include
+ /* make the compiler happy with an empty file */
+ void dummy_snprintf(void);
+ void dummy_snprintf(void) {}
+#endif /* HAVE_SNPRINTF, etc */
+
+#ifdef STDC_HEADERS
+#include
+#endif
+
+#ifdef HAVE_LONG_DOUBLE
+#define LDOUBLE long double
+#else
+#define LDOUBLE double
+#endif
+
+#if !defined HAVE_LONG_LONG && SIZEOF_LONG_LONG
+#define HAVE_LONG_LONG 1
+#endif
+#ifdef HAVE_LONG_LONG
+#define LLONG long long
+#else
+#define LLONG long
+#endif
+
+#ifndef VA_COPY
+#if defined HAVE_VA_COPY || defined va_copy
+#define VA_COPY(dest, src) va_copy(dest, src)
+#else
+#ifdef HAVE___VA_COPY
+#define VA_COPY(dest, src) __va_copy(dest, src)
+#else
+#define VA_COPY(dest, src) (dest) = (src)
+#endif
+#endif
+#endif
+
+/* yes this really must be a ||. Don't muck with this (tridge) */
+#if !defined(HAVE_VSNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
+
+/*
+ * dopr(): poor man's version of doprintf
+ */
+
+/* format read states */
+#define DP_S_DEFAULT 0
+#define DP_S_FLAGS 1
+#define DP_S_MIN 2
+#define DP_S_DOT 3
+#define DP_S_MAX 4
+#define DP_S_MOD 5
+#define DP_S_CONV 6
+#define DP_S_DONE 7
+
+/* format flags - Bits */
+#define DP_F_MINUS (1 << 0)
+#define DP_F_PLUS (1 << 1)
+#define DP_F_SPACE (1 << 2)
+#define DP_F_NUM (1 << 3)
+#define DP_F_ZERO (1 << 4)
+#define DP_F_UP (1 << 5)
+#define DP_F_UNSIGNED (1 << 6)
+
+/* Conversion Flags */
+#define DP_C_CHAR 1
+#define DP_C_SHORT 2
+#define DP_C_LONG 3
+#define DP_C_LDOUBLE 4
+#define DP_C_LLONG 5
+#define DP_C_SIZET 6
+
+/* Chunk types */
+#define CNK_FMT_STR 0
+#define CNK_INT 1
+#define CNK_OCTAL 2
+#define CNK_UINT 3
+#define CNK_HEX 4
+#define CNK_FLOAT 5
+#define CNK_CHAR 6
+#define CNK_STRING 7
+#define CNK_PTR 8
+#define CNK_NUM 9
+#define CNK_PRCNT 10
+
+#define char_to_int(p) ((p)- '0')
+#ifndef MAX
+#define MAX(p,q) (((p) >= (q)) ? (p) : (q))
+#endif
+
+struct pr_chunk {
+ int type; /* chunk type */
+ int num; /* parameter number */
+ int min;
+ int max;
+ int flags;
+ int cflags;
+ int start;
+ int len;
+ LLONG value;
+ LDOUBLE fvalue;
+ char *strvalue;
+ void *pnum;
+ struct pr_chunk *min_star;
+ struct pr_chunk *max_star;
+ struct pr_chunk *next;
+};
+
+struct pr_chunk_x {
+ struct pr_chunk **chunks;
+ int num;
+};
+
+static int dopr(char *buffer, size_t maxlen, const char *format,
+ va_list args_in);
+static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
+ char *value, int flags, int min, int max);
+static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
+ LLONG value, int base, int min, int max, int flags);
+static void fmtfp(char *buffer, size_t *currlen, size_t maxlen,
+ LDOUBLE fvalue, int min, int max, int flags);
+static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);
+static struct pr_chunk *new_chunk(void);
+static int add_cnk_list_entry(struct pr_chunk_x **list,
+ int max_num, struct pr_chunk *chunk);
+
+static int dopr(char *buffer, size_t maxlen, const char *format, va_list args_in)
+{
+ char ch;
+ int state;
+ int pflag;
+ int pnum;
+ int pfirst;
+ size_t currlen;
+ va_list args;
+ const char *base;
+ struct pr_chunk *chunks = NULL;
+ struct pr_chunk *cnk = NULL;
+ struct pr_chunk_x *clist = NULL;
+ int max_pos;
+ int ret = -1;
+
+ VA_COPY(args, args_in);
+
+ state = DP_S_DEFAULT;
+ pfirst = 1;
+ pflag = 0;
+ pnum = 0;
+
+ max_pos = 0;
+ base = format;
+ ch = *format++;
+
+ /* retrieve the string structure as chunks */
+ while (state != DP_S_DONE) {
+ if (ch == '\0')
+ state = DP_S_DONE;
+
+ switch(state) {
+ case DP_S_DEFAULT:
+
+ if (cnk) {
+ cnk->next = new_chunk();
+ cnk = cnk->next;
+ } else {
+ cnk = new_chunk();
+ }
+ if (!cnk) goto done;
+ if (!chunks) chunks = cnk;
+
+ if (ch == '%') {
+ state = DP_S_FLAGS;
+ ch = *format++;
+ } else {
+ cnk->type = CNK_FMT_STR;
+ cnk->start = format - base -1;
+ while ((ch != '\0') && (ch != '%')) ch = *format++;
+ cnk->len = format - base - cnk->start -1;
+ }
+ break;
+ case DP_S_FLAGS:
+ switch (ch) {
+ case '-':
+ cnk->flags |= DP_F_MINUS;
+ ch = *format++;
+ break;
+ case '+':
+ cnk->flags |= DP_F_PLUS;
+ ch = *format++;
+ break;
+ case ' ':
+ cnk->flags |= DP_F_SPACE;
+ ch = *format++;
+ break;
+ case '#':
+ cnk->flags |= DP_F_NUM;
+ ch = *format++;
+ break;
+ case '0':
+ cnk->flags |= DP_F_ZERO;
+ ch = *format++;
+ break;
+ case 'I':
+ /* internationalization not supported yet */
+ ch = *format++;
+ break;
+ default:
+ state = DP_S_MIN;
+ break;
+ }
+ break;
+ case DP_S_MIN:
+ if (isdigit((unsigned char)ch)) {
+ cnk->min = 10 * cnk->min + char_to_int (ch);
+ ch = *format++;
+ } else if (ch == '$') {
+ if (!pfirst && !pflag) {
+ /* parameters must be all positioned or none */
+ goto done;
+ }
+ if (pfirst) {
+ pfirst = 0;
+ pflag = 1;
+ }
+ if (cnk->min == 0) /* what ?? */
+ goto done;
+ cnk->num = cnk->min;
+ cnk->min = 0;
+ ch = *format++;
+ } else if (ch == '*') {
+ if (pfirst) pfirst = 0;
+ cnk->min_star = new_chunk();
+ if (!cnk->min_star) /* out of memory :-( */
+ goto done;
+ cnk->min_star->type = CNK_INT;
+ if (pflag) {
+ int num;
+ ch = *format++;
+ if (!isdigit((unsigned char)ch)) {
+ /* parameters must be all positioned or none */
+ goto done;
+ }
+ for (num = 0; isdigit((unsigned char)ch); ch = *format++) {
+ num = 10 * num + char_to_int(ch);
+ }
+ cnk->min_star->num = num;
+ if (ch != '$') /* what ?? */
+ goto done;
+ } else {
+ cnk->min_star->num = ++pnum;
+ }
+ max_pos = add_cnk_list_entry(&clist, max_pos, cnk->min_star);
+ if (max_pos == 0) /* out of memory :-( */
+ goto done;
+ ch = *format++;
+ state = DP_S_DOT;
+ } else {
+ if (pfirst) pfirst = 0;
+ state = DP_S_DOT;
+ }
+ break;
+ case DP_S_DOT:
+ if (ch == '.') {
+ state = DP_S_MAX;
+ ch = *format++;
+ } else {
+ state = DP_S_MOD;
+ }
+ break;
+ case DP_S_MAX:
+ if (isdigit((unsigned char)ch)) {
+ if (cnk->max < 0)
+ cnk->max = 0;
+ cnk->max = 10 * cnk->max + char_to_int (ch);
+ ch = *format++;
+ } else if (ch == '$') {
+ if (!pfirst && !pflag) {
+ /* parameters must be all positioned or none */
+ goto done;
+ }
+ if (cnk->max <= 0) /* what ?? */
+ goto done;
+ cnk->num = cnk->max;
+ cnk->max = -1;
+ ch = *format++;
+ } else if (ch == '*') {
+ cnk->max_star = new_chunk();
+ if (!cnk->max_star) /* out of memory :-( */
+ goto done;
+ cnk->max_star->type = CNK_INT;
+ if (pflag) {
+ int num;
+ ch = *format++;
+ if (!isdigit((unsigned char)ch)) {
+ /* parameters must be all positioned or none */
+ goto done;
+ }
+ for (num = 0; isdigit((unsigned char)ch); ch = *format++) {
+ num = 10 * num + char_to_int(ch);
+ }
+ cnk->max_star->num = num;
+ if (ch != '$') /* what ?? */
+ goto done;
+ } else {
+ cnk->max_star->num = ++pnum;
+ }
+ max_pos = add_cnk_list_entry(&clist, max_pos, cnk->max_star);
+ if (max_pos == 0) /* out of memory :-( */
+ goto done;
+
+ ch = *format++;
+ state = DP_S_MOD;
+ } else {
+ state = DP_S_MOD;
+ }
+ break;
+ case DP_S_MOD:
+ switch (ch) {
+ case 'h':
+ cnk->cflags = DP_C_SHORT;
+ ch = *format++;
+ if (ch == 'h') {
+ cnk->cflags = DP_C_CHAR;
+ ch = *format++;
+ }
+ break;
+ case 'l':
+ cnk->cflags = DP_C_LONG;
+ ch = *format++;
+ if (ch == 'l') { /* It's a long long */
+ cnk->cflags = DP_C_LLONG;
+ ch = *format++;
+ }
+ break;
+ case 'L':
+ cnk->cflags = DP_C_LDOUBLE;
+ ch = *format++;
+ break;
+ case 'z':
+ cnk->cflags = DP_C_SIZET;
+ ch = *format++;
+ break;
+ default:
+ break;
+ }
+ state = DP_S_CONV;
+ break;
+ case DP_S_CONV:
+ if (cnk->num == 0) cnk->num = ++pnum;
+ max_pos = add_cnk_list_entry(&clist, max_pos, cnk);
+ if (max_pos == 0) /* out of memory :-( */
+ goto done;
+
+ switch (ch) {
+ case 'd':
+ case 'i':
+ cnk->type = CNK_INT;
+ break;
+ case 'o':
+ cnk->type = CNK_OCTAL;
+ cnk->flags |= DP_F_UNSIGNED;
+ break;
+ case 'u':
+ cnk->type = CNK_UINT;
+ cnk->flags |= DP_F_UNSIGNED;
+ break;
+ case 'X':
+ cnk->flags |= DP_F_UP;
+ case 'x':
+ cnk->type = CNK_HEX;
+ cnk->flags |= DP_F_UNSIGNED;
+ break;
+ case 'A':
+ /* hex float not supported yet */
+ case 'E':
+ case 'G':
+ case 'F':
+ cnk->flags |= DP_F_UP;
+ case 'a':
+ /* hex float not supported yet */
+ case 'e':
+ case 'f':
+ case 'g':
+ cnk->type = CNK_FLOAT;
+ break;
+ case 'c':
+ cnk->type = CNK_CHAR;
+ break;
+ case 's':
+ cnk->type = CNK_STRING;
+ break;
+ case 'p':
+ cnk->type = CNK_PTR;
+ cnk->flags |= DP_F_UNSIGNED;
+ break;
+ case 'n':
+ cnk->type = CNK_NUM;
+ break;
+ case '%':
+ cnk->type = CNK_PRCNT;
+ break;
+ default:
+ /* Unknown, bail out*/
+ goto done;
+ }
+ ch = *format++;
+ state = DP_S_DEFAULT;
+ break;
+ case DP_S_DONE:
+ break;
+ default:
+ /* hmm? */
+ break; /* some picky compilers need this */
+ }
+ }
+
+ /* retrieve the format arguments */
+ for (pnum = 0; pnum < max_pos; pnum++) {
+ int i;
+
+ if (clist[pnum].num == 0) {
+ /* ignoring a parameter should not be permitted
+ * all parameters must be matched at least once
+ * BUT seem some system ignore this rule ...
+ * at least my glibc based system does --SSS
+ */
+#ifdef DEBUG_SNPRINTF
+ printf("parameter at position %d not used\n", pnum+1);
+#endif
+ /* eat the parameter */
+ va_arg (args, int);
+ continue;
+ }
+ for (i = 1; i < clist[pnum].num; i++) {
+ if (clist[pnum].chunks[0]->type != clist[pnum].chunks[i]->type) {
+ /* nooo noo no!
+ * all the references to a parameter
+ * must be of the same type
+ */
+ goto done;
+ }
+ }
+ cnk = clist[pnum].chunks[0];
+ switch (cnk->type) {
+ case CNK_INT:
+ if (cnk->cflags == DP_C_SHORT)
+ cnk->value = va_arg (args, int);
+ else if (cnk->cflags == DP_C_LONG)
+ cnk->value = va_arg (args, long int);
+ else if (cnk->cflags == DP_C_LLONG)
+ cnk->value = va_arg (args, LLONG);
+ else if (cnk->cflags == DP_C_SIZET)
+ cnk->value = va_arg (args, ssize_t);
+ else
+ cnk->value = va_arg (args, int);
+
+ for (i = 1; i < clist[pnum].num; i++) {
+ clist[pnum].chunks[i]->value = cnk->value;
+ }
+ break;
+
+ case CNK_OCTAL:
+ case CNK_UINT:
+ case CNK_HEX:
+ if (cnk->cflags == DP_C_SHORT)
+ cnk->value = va_arg (args, unsigned int);
+ else if (cnk->cflags == DP_C_LONG)
+ cnk->value = (unsigned long int)va_arg (args, unsigned long int);
+ else if (cnk->cflags == DP_C_LLONG)
+ cnk->value = (LLONG)va_arg (args, unsigned LLONG);
+ else if (cnk->cflags == DP_C_SIZET)
+ cnk->value = (size_t)va_arg (args, size_t);
+ else
+ cnk->value = (unsigned int)va_arg (args, unsigned int);
+
+ for (i = 1; i < clist[pnum].num; i++) {
+ clist[pnum].chunks[i]->value = cnk->value;
+ }
+ break;
+
+ case CNK_FLOAT:
+ if (cnk->cflags == DP_C_LDOUBLE)
+ cnk->fvalue = va_arg (args, LDOUBLE);
+ else
+ cnk->fvalue = va_arg (args, double);
+
+ for (i = 1; i < clist[pnum].num; i++) {
+ clist[pnum].chunks[i]->fvalue = cnk->fvalue;
+ }
+ break;
+
+ case CNK_CHAR:
+ cnk->value = va_arg (args, int);
+
+ for (i = 1; i < clist[pnum].num; i++) {
+ clist[pnum].chunks[i]->value = cnk->value;
+ }
+ break;
+
+ case CNK_STRING:
+ cnk->strvalue = va_arg (args, char *);
+ if (!cnk->strvalue) cnk->strvalue = "(NULL)";
+
+ for (i = 1; i < clist[pnum].num; i++) {
+ clist[pnum].chunks[i]->strvalue = cnk->strvalue;
+ }
+ break;
+
+ case CNK_PTR:
+ cnk->strvalue = va_arg (args, void *);
+ for (i = 1; i < clist[pnum].num; i++) {
+ clist[pnum].chunks[i]->strvalue = cnk->strvalue;
+ }
+ break;
+
+ case CNK_NUM:
+ if (cnk->cflags == DP_C_CHAR)
+ cnk->pnum = va_arg (args, char *);
+ else if (cnk->cflags == DP_C_SHORT)
+ cnk->pnum = va_arg (args, short int *);
+ else if (cnk->cflags == DP_C_LONG)
+ cnk->pnum = va_arg (args, long int *);
+ else if (cnk->cflags == DP_C_LLONG)
+ cnk->pnum = va_arg (args, LLONG *);
+ else if (cnk->cflags == DP_C_SIZET)
+ cnk->pnum = va_arg (args, ssize_t *);
+ else
+ cnk->pnum = va_arg (args, int *);
+
+ for (i = 1; i < clist[pnum].num; i++) {
+ clist[pnum].chunks[i]->pnum = cnk->pnum;
+ }
+ break;
+
+ case CNK_PRCNT:
+ break;
+
+ default:
+ /* what ?? */
+ goto done;
+ }
+ }
+ /* print out the actual string from chunks */
+ currlen = 0;
+ cnk = chunks;
+ while (cnk) {
+ int len, min, max;
+
+ if (cnk->min_star) min = cnk->min_star->value;
+ else min = cnk->min;
+ if (cnk->max_star) max = cnk->max_star->value;
+ else max = cnk->max;
+
+ switch (cnk->type) {
+
+ case CNK_FMT_STR:
+ if (maxlen != 0 && maxlen > currlen) {
+ if (maxlen > (currlen + cnk->len)) len = cnk->len;
+ else len = maxlen - currlen;
+
+ memcpy(&(buffer[currlen]), &(base[cnk->start]), len);
+ }
+ currlen += cnk->len;
+
+ break;
+
+ case CNK_INT:
+ case CNK_UINT:
+ fmtint (buffer, &currlen, maxlen, cnk->value, 10, min, max, cnk->flags);
+ break;
+
+ case CNK_OCTAL:
+ fmtint (buffer, &currlen, maxlen, cnk->value, 8, min, max, cnk->flags);
+ break;
+
+ case CNK_HEX:
+ fmtint (buffer, &currlen, maxlen, cnk->value, 16, min, max, cnk->flags);
+ break;
+
+ case CNK_FLOAT:
+ fmtfp (buffer, &currlen, maxlen, cnk->fvalue, min, max, cnk->flags);
+ break;
+
+ case CNK_CHAR:
+ dopr_outch (buffer, &currlen, maxlen, cnk->value);
+ break;
+
+ case CNK_STRING:
+ if (max == -1) {
+ max = strlen(cnk->strvalue);
+ }
+ fmtstr (buffer, &currlen, maxlen, cnk->strvalue, cnk->flags, min, max);
+ break;
+
+ case CNK_PTR:
+ fmtint (buffer, &currlen, maxlen, (long)(cnk->strvalue), 16, min, max, cnk->flags);
+ break;
+
+ case CNK_NUM:
+ if (cnk->cflags == DP_C_CHAR)
+ *((char *)(cnk->pnum)) = (char)currlen;
+ else if (cnk->cflags == DP_C_SHORT)
+ *((short int *)(cnk->pnum)) = (short int)currlen;
+ else if (cnk->cflags == DP_C_LONG)
+ *((long int *)(cnk->pnum)) = (long int)currlen;
+ else if (cnk->cflags == DP_C_LLONG)
+ *((LLONG *)(cnk->pnum)) = (LLONG)currlen;
+ else if (cnk->cflags == DP_C_SIZET)
+ *((ssize_t *)(cnk->pnum)) = (ssize_t)currlen;
+ else
+ *((int *)(cnk->pnum)) = (int)currlen;
+ break;
+
+ case CNK_PRCNT:
+ dopr_outch (buffer, &currlen, maxlen, '%');
+ break;
+
+ default:
+ /* what ?? */
+ goto done;
+ }
+ cnk = cnk->next;
+ }
+ if (maxlen != 0) {
+ if (currlen < maxlen - 1)
+ buffer[currlen] = '\0';
+ else if (maxlen > 0)
+ buffer[maxlen - 1] = '\0';
+ }
+ ret = currlen;
+
+done:
+ va_end(args);
+
+ while (chunks) {
+ cnk = chunks->next;
+ free(chunks);
+ chunks = cnk;
+ }
+ if (clist) {
+ for (pnum = 0; pnum < max_pos; pnum++) {
+ if (clist[pnum].chunks) free(clist[pnum].chunks);
+ }
+ free(clist);
+ }
+ return ret;
+}
+
+static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
+ char *value, int flags, int min, int max)
+{
+ int padlen, strln; /* amount to pad */
+ int cnt = 0;
+
+#ifdef DEBUG_SNPRINTF
+ printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value);
+#endif
+ if (value == 0) {
+ value = "";
+ }
+
+ for (strln = 0; strln < max && value[strln]; ++strln); /* strlen */
+ padlen = min - strln;
+ if (padlen < 0)
+ padlen = 0;
+ if (flags & DP_F_MINUS)
+ padlen = -padlen; /* Left Justify */
+
+ while (padlen > 0) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ --padlen;
+ }
+ while (*value && (cnt < max)) {
+ dopr_outch (buffer, currlen, maxlen, *value++);
+ ++cnt;
+ }
+ while (padlen < 0) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ ++padlen;
+ }
+}
+
+/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
+
+static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
+ LLONG value, int base, int min, int max, int flags)
+{
+ int signvalue = 0;
+ unsigned LLONG uvalue;
+ char convert[20];
+ int place = 0;
+ int spadlen = 0; /* amount to space pad */
+ int zpadlen = 0; /* amount to zero pad */
+ int caps = 0;
+
+ if (max < 0)
+ max = 0;
+
+ uvalue = value;
+
+ if(!(flags & DP_F_UNSIGNED)) {
+ if( value < 0 ) {
+ signvalue = '-';
+ uvalue = -value;
+ } else {
+ if (flags & DP_F_PLUS) /* Do a sign (+/i) */
+ signvalue = '+';
+ else if (flags & DP_F_SPACE)
+ signvalue = ' ';
+ }
+ }
+
+ if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
+
+ do {
+ convert[place++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")
+ [uvalue % (unsigned)base ];
+ uvalue = (uvalue / (unsigned)base );
+ } while(uvalue && (place < 20));
+ if (place == 20) place--;
+ convert[place] = 0;
+
+ zpadlen = max - place;
+ spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
+ if (zpadlen < 0) zpadlen = 0;
+ if (spadlen < 0) spadlen = 0;
+ if (flags & DP_F_ZERO) {
+ zpadlen = MAX(zpadlen, spadlen);
+ spadlen = 0;
+ }
+ if (flags & DP_F_MINUS)
+ spadlen = -spadlen; /* Left Justifty */
+
+#ifdef DEBUG_SNPRINTF
+ printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
+ zpadlen, spadlen, min, max, place);
+#endif
+
+ /* Spaces */
+ while (spadlen > 0) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ --spadlen;
+ }
+
+ /* Sign */
+ if (signvalue)
+ dopr_outch (buffer, currlen, maxlen, signvalue);
+
+ /* Zeros */
+ if (zpadlen > 0) {
+ while (zpadlen > 0) {
+ dopr_outch (buffer, currlen, maxlen, '0');
+ --zpadlen;
+ }
+ }
+
+ /* Digits */
+ while (place > 0)
+ dopr_outch (buffer, currlen, maxlen, convert[--place]);
+
+ /* Left Justified spaces */
+ while (spadlen < 0) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ ++spadlen;
+ }
+}
+
+static LDOUBLE abs_val(LDOUBLE value)
+{
+ LDOUBLE result = value;
+
+ if (value < 0)
+ result = -value;
+
+ return result;
+}
+
+static LDOUBLE POW10(int exp)
+{
+ LDOUBLE result = 1;
+
+ while (exp) {
+ result *= 10;
+ exp--;
+ }
+
+ return result;
+}
+
+static LLONG ROUND(LDOUBLE value)
+{
+ LLONG intpart;
+
+ intpart = (LLONG)value;
+ value = value - intpart;
+ if (value >= 0.5) intpart++;
+
+ return intpart;
+}
+
+/* a replacement for modf that doesn't need the math library. Should
+ be portable, but slow */
+static double my_modf(double x0, double *iptr)
+{
+ int i;
+ LLONG l=0;
+ double x = x0;
+ double f = 1.0;
+
+ for (i=0;i<100;i++) {
+ l = (long)x;
+ if (l <= (x+1) && l >= (x-1)) break;
+ x *= 0.1;
+ f *= 10.0;
+ }
+
+ if (i == 100) {
+ /* yikes! the number is beyond what we can handle. What do we do? */
+ (*iptr) = 0;
+ return 0;
+ }
+
+ if (i != 0) {
+ double i2;
+ double ret;
+
+ ret = my_modf(x0-l*f, &i2);
+ (*iptr) = l*f + i2;
+ return ret;
+ }
+
+ (*iptr) = l;
+ return x - (*iptr);
+}
+
+
+static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
+ LDOUBLE fvalue, int min, int max, int flags)
+{
+ int signvalue = 0;
+ double ufvalue;
+ char iconvert[311];
+ char fconvert[311];
+ int iplace = 0;
+ int fplace = 0;
+ int padlen = 0; /* amount to pad */
+ int zpadlen = 0;
+ int caps = 0;
+ int idx;
+ double intpart;
+ double fracpart;
+ double temp;
+
+ /*
+ * AIX manpage says the default is 0, but Solaris says the default
+ * is 6, and sprintf on AIX defaults to 6
+ */
+ if (max < 0)
+ max = 6;
+
+ ufvalue = abs_val (fvalue);
+
+ if (fvalue < 0) {
+ signvalue = '-';
+ } else {
+ if (flags & DP_F_PLUS) { /* Do a sign (+/i) */
+ signvalue = '+';
+ } else {
+ if (flags & DP_F_SPACE)
+ signvalue = ' ';
+ }
+ }
+
+#if 0
+ if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
+#endif
+
+#if 0
+ if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */
+#endif
+
+ /*
+ * Sorry, we only support 9 digits past the decimal because of our
+ * conversion method
+ */
+ if (max > 9)
+ max = 9;
+
+ /* We "cheat" by converting the fractional part to integer by
+ * multiplying by a factor of 10
+ */
+
+ temp = ufvalue;
+ my_modf(temp, &intpart);
+
+ fracpart = ROUND((POW10(max)) * (ufvalue - intpart));
+
+ if (fracpart >= POW10(max)) {
+ intpart++;
+ fracpart -= POW10(max);
+ }
+
+
+ /* Convert integer part */
+ do {
+ temp = intpart*0.1;
+ my_modf(temp, &intpart);
+ idx = (int) ((temp -intpart +0.05)* 10.0);
+ /* idx = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */
+ /* printf ("%llf, %f, %x\n", temp, intpart, idx); */
+ iconvert[iplace++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")[idx];
+ } while (intpart && (iplace < 311));
+ if (iplace == 311) iplace--;
+ iconvert[iplace] = 0;
+
+ /* Convert fractional part */
+ if (fracpart)
+ {
+ do {
+ temp = fracpart*0.1;
+ my_modf(temp, &fracpart);
+ idx = (int) ((temp -fracpart +0.05)* 10.0);
+ /* idx = (int) ((((temp/10) -fracpart) +0.05) *10); */
+ /* printf ("%lf, %lf, %ld\n", temp, fracpart, idx ); */
+ fconvert[fplace++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")[idx];
+ } while(fracpart && (fplace < 311));
+ if (fplace == 311) fplace--;
+ }
+ fconvert[fplace] = 0;
+
+ /* -1 for decimal point, another -1 if we are printing a sign */
+ padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
+ zpadlen = max - fplace;
+ if (zpadlen < 0) zpadlen = 0;
+ if (padlen < 0)
+ padlen = 0;
+ if (flags & DP_F_MINUS)
+ padlen = -padlen; /* Left Justifty */
+
+ if ((flags & DP_F_ZERO) && (padlen > 0)) {
+ if (signvalue) {
+ dopr_outch (buffer, currlen, maxlen, signvalue);
+ --padlen;
+ signvalue = 0;
+ }
+ while (padlen > 0) {
+ dopr_outch (buffer, currlen, maxlen, '0');
+ --padlen;
+ }
+ }
+ while (padlen > 0) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ --padlen;
+ }
+ if (signvalue)
+ dopr_outch (buffer, currlen, maxlen, signvalue);
+
+ while (iplace > 0)
+ dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
+
+#ifdef DEBUG_SNPRINTF
+ printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen);
+#endif
+
+ /*
+ * Decimal point. This should probably use locale to find the correct
+ * char to print out.
+ */
+ if (max > 0) {
+ dopr_outch (buffer, currlen, maxlen, '.');
+
+ while (zpadlen > 0) {
+ dopr_outch (buffer, currlen, maxlen, '0');
+ --zpadlen;
+ }
+
+ while (fplace > 0)
+ dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
+ }
+
+ while (padlen < 0) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ ++padlen;
+ }
+}
+
+static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
+{
+ if (*currlen < maxlen) {
+ buffer[(*currlen)] = c;
+ }
+ (*currlen)++;
+}
+
+static struct pr_chunk *new_chunk(void) {
+ struct pr_chunk *new_c = (struct pr_chunk *)malloc(sizeof(struct pr_chunk));
+
+ if (!new_c)
+ return NULL;
+
+ new_c->type = 0;
+ new_c->num = 0;
+ new_c->min = 0;
+ new_c->min_star = NULL;
+ new_c->max = -1;
+ new_c->max_star = NULL;
+ new_c->flags = 0;
+ new_c->cflags = 0;
+ new_c->start = 0;
+ new_c->len = 0;
+ new_c->value = 0;
+ new_c->fvalue = 0;
+ new_c->strvalue = NULL;
+ new_c->pnum = NULL;
+ new_c->next = NULL;
+
+ return new_c;
+}
+
+static int add_cnk_list_entry(struct pr_chunk_x **list,
+ int max_num, struct pr_chunk *chunk) {
+ struct pr_chunk_x *l;
+ struct pr_chunk **c;
+ int max;
+ int cnum;
+ int i, pos;
+
+ if (chunk->num > max_num) {
+ max = chunk->num;
+
+ if (*list == NULL) {
+ l = (struct pr_chunk_x *)malloc(sizeof(struct pr_chunk_x) * max);
+ pos = 0;
+ } else {
+ l = (struct pr_chunk_x *)realloc(*list, sizeof(struct pr_chunk_x) * max);
+ pos = max_num;
+ }
+ if (l == NULL) {
+ for (i = 0; i < max; i++) {
+ if ((*list)[i].chunks) free((*list)[i].chunks);
+ }
+ return 0;
+ }
+ for (i = pos; i < max; i++) {
+ l[i].chunks = NULL;
+ l[i].num = 0;
+ }
+ } else {
+ l = *list;
+ max = max_num;
+ }
+
+ i = chunk->num - 1;
+ cnum = l[i].num + 1;
+ if (l[i].chunks == NULL) {
+ c = (struct pr_chunk **)malloc(sizeof(struct pr_chunk *) * cnum);
+ } else {
+ c = (struct pr_chunk **)realloc(l[i].chunks, sizeof(struct pr_chunk *) * cnum);
+ }
+ if (c == NULL) {
+ for (i = 0; i < max; i++) {
+ if (l[i].chunks) free(l[i].chunks);
+ }
+ return 0;
+ }
+ c[l[i].num] = chunk;
+ l[i].chunks = c;
+ l[i].num = cnum;
+
+ *list = l;
+ return max;
+}
+
+ int rsync_vsnprintf (char *str, size_t count, const char *fmt, va_list args)
+{
+ return dopr(str, count, fmt, args);
+}
+#define vsnprintf rsync_vsnprintf
+#endif
+
+/* yes this really must be a ||. Don't muck with this (tridge)
+ *
+ * The logic for these two is that we need our own definition if the
+ * OS *either* has no definition of *sprintf, or if it does have one
+ * that doesn't work properly according to the autoconf test.
+ */
+#if !defined(HAVE_SNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
+int rsync_snprintf(char *str,size_t count,const char *fmt,...)
+{
+ size_t ret;
+ va_list ap;
+
+ va_start(ap, fmt);
+ ret = vsnprintf(str, count, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+#define snprintf rsync_snprintf
+#endif
+
+#ifndef HAVE_VASPRINTF
+ int vasprintf(char **ptr, const char *format, va_list ap)
+{
+ int ret;
+ va_list ap2;
+
+ VA_COPY(ap2, ap);
+ ret = vsnprintf(NULL, 0, format, ap2);
+ va_end(ap2);
+ if (ret < 0) return ret;
+
+ (*ptr) = (char *)malloc(ret+1);
+ if (!*ptr) return -1;
+
+ VA_COPY(ap2, ap);
+ ret = vsnprintf(*ptr, ret+1, format, ap2);
+ va_end(ap2);
+
+ return ret;
+}
+#endif
+
+
+#ifndef HAVE_ASPRINTF
+ int asprintf(char **ptr, const char *format, ...)
+{
+ va_list ap;
+ int ret;
+
+ *ptr = NULL;
+ va_start(ap, format);
+ ret = vasprintf(ptr, format, ap);
+ va_end(ap);
+
+ return ret;
+}
+#endif
+
+#ifdef TEST_SNPRINTF
+
+ int sprintf(char *str,const char *fmt,...);
+ int printf(const char *fmt,...);
+
+ int main (void)
+{
+ char buf1[1024];
+ char buf2[1024];
+ char *buf3;
+ char *fp_fmt[] = {
+ "%1.1f",
+ "%-1.5f",
+ "%1.5f",
+ "%123.9f",
+ "%10.5f",
+ "% 10.5f",
+ "%+22.9f",
+ "%+4.9f",
+ "%01.3f",
+ "%4f",
+ "%3.1f",
+ "%3.2f",
+ "%.0f",
+ "%f",
+ "%-8.8f",
+ "%-9.9f",
+ NULL
+ };
+ double fp_nums[] = { 6442452944.1234, -1.5, 134.21, 91340.2, 341.1234, 203.9, 0.96, 0.996,
+ 0.9996, 1.996, 4.136, 5.030201, 0.00205,
+ /* END LIST */ 0};
+ char *int_fmt[] = {
+ "%-1.5d",
+ "%1.5d",
+ "%123.9d",
+ "%5.5d",
+ "%10.5d",
+ "% 10.5d",
+ "%+22.33d",
+ "%01.3d",
+ "%4d",
+ "%d",
+ NULL
+ };
+ long int_nums[] = { -1, 134, 91340, 341, 0203, 1234567890, 0};
+ char *str_fmt[] = {
+ "%10.5s",
+ "%-10.5s",
+ "%5.10s",
+ "%-5.10s",
+ "%10.1s",
+ "%0.10s",
+ "%10.0s",
+ "%1.10s",
+ "%s",
+ "%.1s",
+ "%.10s",
+ "%10s",
+ NULL
+ };
+ char *str_vals[] = {"hello", "a", "", "a longer string", NULL};
+#ifdef HAVE_LONG_LONG
+ char *ll_fmt[] = {
+ "%llu",
+ NULL
+ };
+ LLONG ll_nums[] = { 134, 91340, 341, 0203, 1234567890, 128006186140000000LL, 0};
+#endif
+ int x, y;
+ int fail = 0;
+ int num = 0;
+ int l1, l2;
+ char *ss_fmt[] = {
+ "%zd",
+ "%zu",
+ NULL
+ };
+ size_t ss_nums[] = {134, 91340, 123456789, 0203, 1234567890, 0};
+
+ printf ("Testing snprintf format codes against system sprintf...\n");
+
+ for (x = 0; fp_fmt[x] ; x++) {
+ for (y = 0; fp_nums[y] != 0 ; y++) {
+ buf1[0] = buf2[0] = '\0';
+ l1 = snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]);
+ l2 = sprintf (buf2, fp_fmt[x], fp_nums[y]);
+ buf1[1023] = buf2[1023] = '\0';
+ if (strcmp (buf1, buf2) || (l1 != l2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
+ fp_fmt[x], l1, buf1, l2, buf2);
+ fail++;
+ }
+ num++;
+ }
+ }
+
+ for (x = 0; int_fmt[x] ; x++) {
+ for (y = 0; int_nums[y] != 0 ; y++) {
+ buf1[0] = buf2[0] = '\0';
+ l1 = snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]);
+ l2 = sprintf (buf2, int_fmt[x], int_nums[y]);
+ buf1[1023] = buf2[1023] = '\0';
+ if (strcmp (buf1, buf2) || (l1 != l2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
+ int_fmt[x], l1, buf1, l2, buf2);
+ fail++;
+ }
+ num++;
+ }
+ }
+
+ for (x = 0; str_fmt[x] ; x++) {
+ for (y = 0; str_vals[y] != 0 ; y++) {
+ buf1[0] = buf2[0] = '\0';
+ l1 = snprintf(buf1, sizeof(buf1), str_fmt[x], str_vals[y]);
+ l2 = sprintf (buf2, str_fmt[x], str_vals[y]);
+ buf1[1023] = buf2[1023] = '\0';
+ if (strcmp (buf1, buf2) || (l1 != l2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
+ str_fmt[x], l1, buf1, l2, buf2);
+ fail++;
+ }
+ num++;
+ }
+ }
+
+#ifdef HAVE_LONG_LONG
+ for (x = 0; ll_fmt[x] ; x++) {
+ for (y = 0; ll_nums[y] != 0 ; y++) {
+ buf1[0] = buf2[0] = '\0';
+ l1 = snprintf(buf1, sizeof(buf1), ll_fmt[x], ll_nums[y]);
+ l2 = sprintf (buf2, ll_fmt[x], ll_nums[y]);
+ buf1[1023] = buf2[1023] = '\0';
+ if (strcmp (buf1, buf2) || (l1 != l2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
+ ll_fmt[x], l1, buf1, l2, buf2);
+ fail++;
+ }
+ num++;
+ }
+ }
+#endif
+
+#define BUFSZ 2048
+
+ buf1[0] = buf2[0] = '\0';
+ if ((buf3 = malloc(BUFSZ)) == NULL) {
+ fail++;
+ } else {
+ num++;
+ memset(buf3, 'a', BUFSZ);
+ snprintf(buf1, sizeof(buf1), "%.*s", 1, buf3);
+ buf1[1023] = '\0';
+ if (strcmp(buf1, "a") != 0) {
+ printf("length limit buf1 '%s' expected 'a'\n", buf1);
+ fail++;
+ }
+ }
+
+ buf1[0] = buf2[0] = '\0';
+ l1 = snprintf(buf1, sizeof(buf1), "%4$*1$d %2$s %3$*1$.*1$f", 3, "pos test", 12.3456, 9);
+ l2 = sprintf(buf2, "%4$*1$d %2$s %3$*1$.*1$f", 3, "pos test", 12.3456, 9);
+ buf1[1023] = buf2[1023] = '\0';
+ if (strcmp(buf1, buf2) || (l1 != l2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
+ "%4$*1$d %2$s %3$*1$.*1$f", l1, buf1, l2, buf2);
+ fail++;
+ }
+
+ buf1[0] = buf2[0] = '\0';
+ l1 = snprintf(buf1, sizeof(buf1), "%4$*4$d %2$s %3$*4$.*4$f", 3, "pos test", 12.3456, 9);
+ l2 = sprintf(buf2, "%4$*4$d %2$s %3$*4$.*4$f", 3, "pos test", 12.3456, 9);
+ buf1[1023] = buf2[1023] = '\0';
+ if (strcmp(buf1, buf2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
+ "%4$*1$d %2$s %3$*1$.*1$f", l1, buf1, l2, buf2);
+ fail++;
+ }
+
+ for (x = 0; ss_fmt[x] ; x++) {
+ for (y = 0; ss_nums[y] != 0 ; y++) {
+ buf1[0] = buf2[0] = '\0';
+ l1 = snprintf(buf1, sizeof(buf1), ss_fmt[x], ss_nums[y]);
+ l2 = sprintf (buf2, ss_fmt[x], ss_nums[y]);
+ buf1[1023] = buf2[1023] = '\0';
+ if (strcmp (buf1, buf2) || (l1 != l2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
+ ss_fmt[x], l1, buf1, l2, buf2);
+ fail++;
+ }
+ num++;
+ }
+ }
+#if 0
+ buf1[0] = buf2[0] = '\0';
+ l1 = snprintf(buf1, sizeof(buf1), "%lld", (LLONG)1234567890);
+ l2 = sprintf(buf2, "%lld", (LLONG)1234567890);
+ buf1[1023] = buf2[1023] = '\0';
+ if (strcmp(buf1, buf2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
+ "%lld", l1, buf1, l2, buf2);
+ fail++;
+ }
+
+ buf1[0] = buf2[0] = '\0';
+ l1 = snprintf(buf1, sizeof(buf1), "%Lf", (LDOUBLE)890.1234567890123);
+ l2 = sprintf(buf2, "%Lf", (LDOUBLE)890.1234567890123);
+ buf1[1023] = buf2[1023] = '\0';
+ if (strcmp(buf1, buf2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
+ "%Lf", l1, buf1, l2, buf2);
+ fail++;
+ }
+#endif
+ printf ("%d tests failed out of %d.\n", fail, num);
+
+ printf("seeing how many digits we support\n");
+ {
+ double v0 = 0.12345678901234567890123456789012345678901;
+ for (x=0; x<100; x++) {
+ double p = pow(10, x);
+ double r = v0*p;
+ snprintf(buf1, sizeof(buf1), "%1.1f", r);
+ sprintf(buf2, "%1.1f", r);
+ if (strcmp(buf1, buf2)) {
+ printf("we seem to support %d digits\n", x-1);
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+#endif /* TEST_SNPRINTF */
diff --git a/rsync/lib/sysacls.c b/rsync/lib/sysacls.c
new file mode 100644
index 0000000..6ccfe43
--- /dev/null
+++ b/rsync/lib/sysacls.c
@@ -0,0 +1,2796 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Based on the Samba ACL support code.
+ * Copyright (C) Jeremy Allison 2000.
+ * Copyright (C) 2007-2014 Wayne Davison
+ *
+ * The permission functions have been changed to get/set all bits via
+ * one call. Some functions that rsync doesn't need were also removed.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "sysacls.h"
+
+#ifdef SUPPORT_ACLS
+
+#ifdef DEBUG
+#undef DEBUG
+#endif
+#define DEBUG(x,y)
+
+void SAFE_FREE(void *mem)
+{
+ if (mem)
+ free(mem);
+}
+
+/*
+ This file wraps all differing system ACL interfaces into a consistent
+ one based on the POSIX interface. It also returns the correct errors
+ for older UNIX systems that don't support ACLs.
+
+ The interfaces that each ACL implementation must support are as follows :
+
+ int sys_acl_get_entry( SMB_ACL_T theacl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+ int sys_acl_get_tag_type( SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+ int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *bits_p, id_t *u_g_id_p)
+ SMB_ACL_T sys_acl_get_file( const char *path_p, SMB_ACL_TYPE_T type)
+ SMB_ACL_T sys_acl_get_fd(int fd)
+ SMB_ACL_T sys_acl_init( int count)
+ int sys_acl_create_entry( SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+ int sys_acl_set_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tag_type, uint32 bits, id_t u_g_id)
+ int sys_acl_set_access_bits(SMB_ACL_ENTRY_T entry, uint32 bits)
+ int sys_acl_valid( SMB_ACL_T theacl )
+ int sys_acl_set_file( const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+ int sys_acl_set_fd( int fd, SMB_ACL_T theacl)
+ int sys_acl_delete_def_file(const char *path)
+ int sys_acl_free_acl(SMB_ACL_T posix_acl)
+
+*/
+
+#if defined(HAVE_POSIX_ACLS) /*--------------------------------------------*/
+
+/* Identity mapping - easy. */
+
+int sys_acl_get_entry( SMB_ACL_T the_acl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ return acl_get_entry( the_acl, entry_id, entry_p);
+}
+
+int sys_acl_get_tag_type( SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+ return acl_get_tag_type( entry_d, tag_type_p);
+}
+
+SMB_ACL_T sys_acl_get_file( const char *path_p, SMB_ACL_TYPE_T type)
+{
+ return acl_get_file( path_p, type);
+}
+
+#if 0
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ return acl_get_fd(fd);
+}
+#endif
+
+#if defined(HAVE_ACL_GET_PERM_NP)
+#define acl_get_perm(p, b) acl_get_perm_np(p, b)
+#endif
+
+int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *bits_p, id_t *u_g_id_p)
+{
+ acl_permset_t permset;
+
+ if (acl_get_tag_type(entry, tag_type_p) != 0
+ || acl_get_permset(entry, &permset) != 0)
+ return -1;
+
+ *bits_p = (acl_get_perm(permset, ACL_READ) ? 4 : 0)
+ | (acl_get_perm(permset, ACL_WRITE) ? 2 : 0)
+ | (acl_get_perm(permset, ACL_EXECUTE) ? 1 : 0);
+
+ if (*tag_type_p == SMB_ACL_USER || *tag_type_p == SMB_ACL_GROUP) {
+ void *qual;
+ if ((qual = acl_get_qualifier(entry)) == NULL)
+ return -1;
+ *u_g_id_p = *(id_t*)qual;
+ acl_free(qual);
+ }
+
+ return 0;
+}
+
+SMB_ACL_T sys_acl_init( int count)
+{
+ return acl_init(count);
+}
+
+int sys_acl_create_entry( SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+ return acl_create_entry(pacl, pentry);
+}
+
+int sys_acl_set_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tag_type, uint32 bits, id_t u_g_id)
+{
+ if (acl_set_tag_type(entry, tag_type) != 0)
+ return -1;
+
+ if (tag_type == SMB_ACL_USER || tag_type == SMB_ACL_GROUP) {
+ if (acl_set_qualifier(entry, (void*)&u_g_id) != 0)
+ return -1;
+ }
+
+ return sys_acl_set_access_bits(entry, bits);
+}
+
+int sys_acl_set_access_bits(SMB_ACL_ENTRY_T entry, uint32 bits)
+{
+ acl_permset_t permset;
+ int rc;
+ if ((rc = acl_get_permset(entry, &permset)) != 0)
+ return rc;
+ acl_clear_perms(permset);
+ if (bits & 4)
+ acl_add_perm(permset, ACL_READ);
+ if (bits & 2)
+ acl_add_perm(permset, ACL_WRITE);
+ if (bits & 1)
+ acl_add_perm(permset, ACL_EXECUTE);
+ return acl_set_permset(entry, permset);
+}
+
+int sys_acl_valid( SMB_ACL_T theacl )
+{
+ return acl_valid(theacl);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+ return acl_set_file(name, acltype, theacl);
+}
+
+#if 0
+int sys_acl_set_fd( int fd, SMB_ACL_T theacl)
+{
+ return acl_set_fd(fd, theacl);
+}
+#endif
+
+int sys_acl_delete_def_file(const char *name)
+{
+ return acl_delete_def_file(name);
+}
+
+int sys_acl_free_acl(SMB_ACL_T the_acl)
+{
+ return acl_free(the_acl);
+}
+
+#elif defined(HAVE_TRU64_ACLS) /*--------------------------------------------*/
+/*
+ * The interface to DEC/Compaq Tru64 UNIX ACLs
+ * is based on Draft 13 of the POSIX spec which is
+ * slightly different from the Draft 16 interface.
+ *
+ * Also, some of the permset manipulation functions
+ * such as acl_clear_perm() and acl_add_perm() appear
+ * to be broken on Tru64 so we have to manipulate
+ * the permission bits in the permset directly.
+ */
+int sys_acl_get_entry( SMB_ACL_T the_acl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ SMB_ACL_ENTRY_T entry;
+
+ if (entry_id == SMB_ACL_FIRST_ENTRY && acl_first_entry(the_acl) != 0) {
+ return -1;
+ }
+
+ errno = 0;
+ if ((entry = acl_get_entry(the_acl)) != NULL) {
+ *entry_p = entry;
+ return 1;
+ }
+
+ return errno ? -1 : 0;
+}
+
+int sys_acl_get_tag_type( SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+ return acl_get_tag_type( entry_d, tag_type_p);
+}
+
+SMB_ACL_T sys_acl_get_file( const char *path_p, SMB_ACL_TYPE_T type)
+{
+ return acl_get_file((char *)path_p, type);
+}
+
+#if 0
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ return acl_get_fd(fd, ACL_TYPE_ACCESS);
+}
+#endif
+
+int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *bits_p, id_t *u_g_id_p)
+{
+ acl_permset_t permset;
+
+ if (acl_get_tag_type(entry, tag_type_p) != 0
+ || acl_get_permset(entry, &permset) != 0)
+ return -1;
+
+ *bits_p = *permset & 7; /* Tru64 doesn't have acl_get_perm() */
+
+ if (*tag_type_p == SMB_ACL_USER || *tag_type_p == SMB_ACL_GROUP) {
+ void *qual;
+ if ((qual = acl_get_qualifier(entry)) == NULL)
+ return -1;
+ *u_g_id_p = *(id_t*)qual;
+ acl_free_qualifier(qual, *tag_type_p);
+ }
+
+ return 0;
+}
+
+SMB_ACL_T sys_acl_init( int count)
+{
+ return acl_init(count);
+}
+
+int sys_acl_create_entry( SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+ SMB_ACL_ENTRY_T entry;
+
+ if ((entry = acl_create_entry(pacl)) == NULL) {
+ return -1;
+ }
+
+ *pentry = entry;
+ return 0;
+}
+
+int sys_acl_set_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tag_type, uint32 bits, id_t u_g_id)
+{
+ if (acl_set_tag_type(entry, tag_type) != 0)
+ return -1;
+
+ if (tag_type == SMB_ACL_USER || tag_type == SMB_ACL_GROUP) {
+ if (acl_set_qualifier(entry, (void*)&u_g_id) != 0)
+ return -1;
+ }
+
+ return sys_acl_set_access_bits(entry, bits);
+}
+
+int sys_acl_set_access_bits(SMB_ACL_ENTRY_T entry, uint32 bits)
+{
+ acl_permset_t permset;
+ int rc;
+ if ((rc = acl_get_permset(entry, &permset)) != 0)
+ return rc;
+ *permset = bits & 7;
+ return acl_set_permset(entry, permset);
+}
+
+int sys_acl_valid( SMB_ACL_T theacl )
+{
+ acl_entry_t entry;
+
+ return acl_valid(theacl, &entry);
+}
+
+int sys_acl_set_file( const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+ return acl_set_file((char *)name, acltype, theacl);
+}
+
+#if 0
+int sys_acl_set_fd( int fd, SMB_ACL_T theacl)
+{
+ return acl_set_fd(fd, ACL_TYPE_ACCESS, theacl);
+}
+#endif
+
+int sys_acl_delete_def_file(const char *name)
+{
+ return acl_delete_def_file((char *)name);
+}
+
+int sys_acl_free_acl(SMB_ACL_T the_acl)
+{
+ return acl_free(the_acl);
+}
+
+#elif defined(HAVE_UNIXWARE_ACLS) || defined(HAVE_SOLARIS_ACLS) /*-----------*/
+
+/*
+ * Donated by Michael Davidson for UnixWare / OpenUNIX.
+ * Modified by Toomas Soome for Solaris.
+ */
+
+/*
+ * Note that while this code implements sufficient functionality
+ * to support the sys_acl_* interfaces it does not provide all
+ * of the semantics of the POSIX ACL interfaces.
+ *
+ * In particular, an ACL entry descriptor (SMB_ACL_ENTRY_T) returned
+ * from a call to sys_acl_get_entry() should not be assumed to be
+ * valid after calling any of the following functions, which may
+ * reorder the entries in the ACL.
+ *
+ * sys_acl_valid()
+ * sys_acl_set_file()
+ * sys_acl_set_fd()
+ */
+
+/*
+ * The only difference between Solaris and UnixWare / OpenUNIX is
+ * that the #defines for the ACL operations have different names
+ */
+#if defined(HAVE_UNIXWARE_ACLS)
+
+#define SETACL ACL_SET
+#define GETACL ACL_GET
+#define GETACLCNT ACL_CNT
+
+#endif
+
+
+int sys_acl_get_entry(SMB_ACL_T acl_d, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ if (entry_id != SMB_ACL_FIRST_ENTRY && entry_id != SMB_ACL_NEXT_ENTRY) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_p == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_id == SMB_ACL_FIRST_ENTRY) {
+ acl_d->next = 0;
+ }
+
+ if (acl_d->next < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->next >= acl_d->count) {
+ return 0;
+ }
+
+ *entry_p = &acl_d->acl[acl_d->next++];
+
+ return 1;
+}
+
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *type_p)
+{
+ *type_p = entry_d->a_type;
+
+ return 0;
+}
+
+/*
+ * There is no way of knowing what size the ACL returned by
+ * GETACL will be unless you first call GETACLCNT which means
+ * making an additional system call.
+ *
+ * In the hope of avoiding the cost of the additional system
+ * call in most cases, we initially allocate enough space for
+ * an ACL with INITIAL_ACL_SIZE entries. If this turns out to
+ * be too small then we use GETACLCNT to find out the actual
+ * size, reallocate the ACL buffer, and then call GETACL again.
+ */
+
+#define INITIAL_ACL_SIZE 16
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+ SMB_ACL_T acl_d;
+ int count; /* # of ACL entries allocated */
+ int naccess; /* # of access ACL entries */
+ int ndefault; /* # of default ACL entries */
+
+ if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ count = INITIAL_ACL_SIZE;
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+
+ /*
+ * If there isn't enough space for the ACL entries we use
+ * GETACLCNT to determine the actual number of ACL entries
+ * reallocate and try again. This is in a loop because it
+ * is possible that someone else could modify the ACL and
+ * increase the number of entries between the call to
+ * GETACLCNT and the call to GETACL.
+ */
+ while ((count = acl(path_p, GETACL, count, &acl_d->acl[0])) < 0
+ && errno == ENOSPC) {
+
+ sys_acl_free_acl(acl_d);
+
+ if ((count = acl(path_p, GETACLCNT, 0, NULL)) < 0) {
+ return NULL;
+ }
+
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+ }
+
+ if (count < 0) {
+ sys_acl_free_acl(acl_d);
+ return NULL;
+ }
+
+ /*
+ * calculate the number of access and default ACL entries
+ *
+ * Note: we assume that the acl() system call returned a
+ * well formed ACL which is sorted so that all of the
+ * access ACL entries preceed any default ACL entries
+ */
+ for (naccess = 0; naccess < count; naccess++) {
+ if (acl_d->acl[naccess].a_type & ACL_DEFAULT)
+ break;
+ }
+ ndefault = count - naccess;
+
+ /*
+ * if the caller wants the default ACL we have to copy
+ * the entries down to the start of the acl[] buffer
+ * and mask out the ACL_DEFAULT flag from the type field
+ */
+ if (type == SMB_ACL_TYPE_DEFAULT) {
+ int i, j;
+
+ for (i = 0, j = naccess; i < ndefault; i++, j++) {
+ acl_d->acl[i] = acl_d->acl[j];
+ acl_d->acl[i].a_type &= ~ACL_DEFAULT;
+ }
+
+ acl_d->count = ndefault;
+ } else {
+ acl_d->count = naccess;
+ }
+
+ return acl_d;
+}
+
+#if 0
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ SMB_ACL_T acl_d;
+ int count; /* # of ACL entries allocated */
+ int naccess; /* # of access ACL entries */
+
+ count = INITIAL_ACL_SIZE;
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+
+ while ((count = facl(fd, GETACL, count, &acl_d->acl[0])) < 0
+ && errno == ENOSPC) {
+
+ sys_acl_free_acl(acl_d);
+
+ if ((count = facl(fd, GETACLCNT, 0, NULL)) < 0) {
+ return NULL;
+ }
+
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+ }
+
+ if (count < 0) {
+ sys_acl_free_acl(acl_d);
+ return NULL;
+ }
+
+ /*
+ * calculate the number of access ACL entries
+ */
+ for (naccess = 0; naccess < count; naccess++) {
+ if (acl_d->acl[naccess].a_type & ACL_DEFAULT)
+ break;
+ }
+
+ acl_d->count = naccess;
+
+ return acl_d;
+}
+#endif
+
+int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *bits_p, id_t *u_g_id_p)
+{
+ *tag_type_p = entry->a_type;
+
+ *bits_p = entry->a_perm;
+
+ if (*tag_type_p == SMB_ACL_USER || *tag_type_p == SMB_ACL_GROUP)
+ *u_g_id_p = entry->a_id;
+
+ return 0;
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+ SMB_ACL_T a;
+
+ if (count < 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /*
+ * note that since the definition of the structure pointed
+ * to by the SMB_ACL_T includes the first element of the
+ * acl[] array, this actually allocates an ACL with room
+ * for (count+1) entries
+ */
+ if ((a = (SMB_ACL_T)SMB_MALLOC(sizeof a[0] + count * sizeof (struct acl))) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ a->size = count + 1;
+ a->count = 0;
+ a->next = -1;
+
+ return a;
+}
+
+
+int sys_acl_create_entry(SMB_ACL_T *acl_p, SMB_ACL_ENTRY_T *entry_p)
+{
+ SMB_ACL_T acl_d;
+ SMB_ACL_ENTRY_T entry_d;
+
+ if (acl_p == NULL || entry_p == NULL || (acl_d = *acl_p) == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->count >= acl_d->size) {
+ errno = ENOSPC;
+ return -1;
+ }
+
+ entry_d = &acl_d->acl[acl_d->count++];
+ entry_d->a_type = 0;
+ entry_d->a_id = -1;
+ entry_d->a_perm = 0;
+ *entry_p = entry_d;
+
+ return 0;
+}
+
+int sys_acl_set_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tag_type, uint32 bits, id_t u_g_id)
+{
+ entry->a_type = tag_type;
+
+ if (tag_type == SMB_ACL_USER || tag_type == SMB_ACL_GROUP)
+ entry->a_id = u_g_id;
+
+ entry->a_perm = bits;
+
+ return 0;
+}
+
+int sys_acl_set_access_bits(SMB_ACL_ENTRY_T entry_d, uint32 bits)
+{
+ entry_d->a_perm = bits;
+ return 0;
+}
+
+/*
+ * sort the ACL and check it for validity
+ *
+ * if it's a minimal ACL with only 4 entries then we
+ * need to recalculate the mask permissions to make
+ * sure that they are the same as the GROUP_OBJ
+ * permissions as required by the UnixWare acl() system call.
+ *
+ * (note: since POSIX allows minimal ACLs which only contain
+ * 3 entries - ie there is no mask entry - we should, in theory,
+ * check for this and add a mask entry if necessary - however
+ * we "know" that the caller of this interface always specifies
+ * a mask so, in practice "this never happens" (tm) - if it *does*
+ * happen aclsort() will fail and return an error and someone will
+ * have to fix it ...)
+ */
+
+static int acl_sort(SMB_ACL_T acl_d)
+{
+ int fixmask = (acl_d->count <= 4);
+
+ if (aclsort(acl_d->count, fixmask, acl_d->acl) != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+int sys_acl_valid(SMB_ACL_T acl_d)
+{
+ return acl_sort(acl_d);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T type, SMB_ACL_T acl_d)
+{
+ struct stat s;
+ struct acl *acl_p;
+ int acl_count;
+ struct acl *acl_buf = NULL;
+ int ret;
+
+ if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_sort(acl_d) != 0) {
+ return -1;
+ }
+
+ acl_p = &acl_d->acl[0];
+ acl_count = acl_d->count;
+
+ /*
+ * if it's a directory there is extra work to do
+ * since the acl() system call will replace both
+ * the access ACLs and the default ACLs (if any)
+ */
+ if (stat(name, &s) != 0) {
+ return -1;
+ }
+ if (S_ISDIR(s.st_mode)) {
+ SMB_ACL_T acc_acl;
+ SMB_ACL_T def_acl;
+ SMB_ACL_T tmp_acl;
+ int i;
+
+ if (type == SMB_ACL_TYPE_ACCESS) {
+ acc_acl = acl_d;
+ def_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_DEFAULT);
+
+ } else {
+ def_acl = acl_d;
+ acc_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_ACCESS);
+ }
+
+ if (tmp_acl == NULL) {
+ return -1;
+ }
+
+ /*
+ * allocate a temporary buffer for the complete ACL
+ */
+ acl_count = acc_acl->count + def_acl->count;
+ acl_p = acl_buf = SMB_MALLOC_ARRAY(struct acl, acl_count);
+
+ if (acl_buf == NULL) {
+ sys_acl_free_acl(tmp_acl);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /*
+ * copy the access control and default entries into the buffer
+ */
+ memcpy(&acl_buf[0], &acc_acl->acl[0],
+ acc_acl->count * sizeof(acl_buf[0]));
+
+ memcpy(&acl_buf[acc_acl->count], &def_acl->acl[0],
+ def_acl->count * sizeof(acl_buf[0]));
+
+ /*
+ * set the ACL_DEFAULT flag on the default entries
+ */
+ for (i = acc_acl->count; i < acl_count; i++) {
+ acl_buf[i].a_type |= ACL_DEFAULT;
+ }
+
+ sys_acl_free_acl(tmp_acl);
+
+ } else if (type != SMB_ACL_TYPE_ACCESS) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ret = acl(name, SETACL, acl_count, acl_p);
+
+ SAFE_FREE(acl_buf);
+
+ return ret;
+}
+
+#if 0
+int sys_acl_set_fd(int fd, SMB_ACL_T acl_d)
+{
+ if (acl_sort(acl_d) != 0) {
+ return -1;
+ }
+
+ return facl(fd, SETACL, acl_d->count, &acl_d->acl[0]);
+}
+#endif
+
+int sys_acl_delete_def_file(const char *path)
+{
+ SMB_ACL_T acl_d;
+ int ret;
+
+ /*
+ * fetching the access ACL and rewriting it has
+ * the effect of deleting the default ACL
+ */
+ if ((acl_d = sys_acl_get_file(path, SMB_ACL_TYPE_ACCESS)) == NULL) {
+ return -1;
+ }
+
+ ret = acl(path, SETACL, acl_d->count, acl_d->acl);
+
+ sys_acl_free_acl(acl_d);
+
+ return ret;
+}
+
+int sys_acl_free_acl(SMB_ACL_T acl_d)
+{
+ SAFE_FREE(acl_d);
+ return 0;
+}
+
+#elif defined(HAVE_HPUX_ACLS) /*---------------------------------------------*/
+
+#include
+
+/*
+ * Based on the Solaris/SCO code - with modifications.
+ */
+
+/*
+ * Note that while this code implements sufficient functionality
+ * to support the sys_acl_* interfaces it does not provide all
+ * of the semantics of the POSIX ACL interfaces.
+ *
+ * In particular, an ACL entry descriptor (SMB_ACL_ENTRY_T) returned
+ * from a call to sys_acl_get_entry() should not be assumed to be
+ * valid after calling any of the following functions, which may
+ * reorder the entries in the ACL.
+ *
+ * sys_acl_valid()
+ * sys_acl_set_file()
+ * sys_acl_set_fd()
+ */
+
+/* This checks if the POSIX ACL system call is defined */
+/* which basically corresponds to whether JFS 3.3 or */
+/* higher is installed. If acl() was called when it */
+/* isn't defined, it causes the process to core dump */
+/* so it is important to check this and avoid acl() */
+/* calls if it isn't there. */
+
+static BOOL hpux_acl_call_presence(void)
+{
+
+ shl_t handle = NULL;
+ void *value;
+ int ret_val=0;
+ static BOOL already_checked=0;
+
+ if(already_checked)
+ return True;
+
+
+ ret_val = shl_findsym(&handle, "acl", TYPE_PROCEDURE, &value);
+
+ if(ret_val != 0) {
+ DEBUG(5, ("hpux_acl_call_presence: shl_findsym() returned %d, errno = %d, error %s\n",
+ ret_val, errno, strerror(errno)));
+ DEBUG(5,("hpux_acl_call_presence: acl() system call is not present. Check if you have JFS 3.3 and above?\n"));
+ return False;
+ }
+
+ DEBUG(10,("hpux_acl_call_presence: acl() system call is present. We have JFS 3.3 or above \n"));
+
+ already_checked = True;
+ return True;
+}
+
+int sys_acl_get_entry(SMB_ACL_T acl_d, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ if (entry_id != SMB_ACL_FIRST_ENTRY && entry_id != SMB_ACL_NEXT_ENTRY) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_p == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_id == SMB_ACL_FIRST_ENTRY) {
+ acl_d->next = 0;
+ }
+
+ if (acl_d->next < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->next >= acl_d->count) {
+ return 0;
+ }
+
+ *entry_p = &acl_d->acl[acl_d->next++];
+
+ return 1;
+}
+
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *type_p)
+{
+ *type_p = entry_d->a_type;
+
+ return 0;
+}
+
+/*
+ * There is no way of knowing what size the ACL returned by
+ * ACL_GET will be unless you first call ACL_CNT which means
+ * making an additional system call.
+ *
+ * In the hope of avoiding the cost of the additional system
+ * call in most cases, we initially allocate enough space for
+ * an ACL with INITIAL_ACL_SIZE entries. If this turns out to
+ * be too small then we use ACL_CNT to find out the actual
+ * size, reallocate the ACL buffer, and then call ACL_GET again.
+ */
+
+#define INITIAL_ACL_SIZE 16
+
+#ifndef NACLENTRIES
+#define NACLENTRIES 0
+#endif
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+ SMB_ACL_T acl_d;
+ int count; /* # of ACL entries allocated */
+ int naccess; /* # of access ACL entries */
+ int ndefault; /* # of default ACL entries */
+
+ if(hpux_acl_call_presence() == False) {
+ /* Looks like we don't have the acl() system call on HPUX.
+ * May be the system doesn't have the latest version of JFS.
+ */
+ return NULL;
+ }
+
+ if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ count = INITIAL_ACL_SIZE;
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+
+ /*
+ * If there isn't enough space for the ACL entries we use
+ * ACL_CNT to determine the actual number of ACL entries
+ * reallocate and try again. This is in a loop because it
+ * is possible that someone else could modify the ACL and
+ * increase the number of entries between the call to
+ * ACL_CNT and the call to ACL_GET.
+ */
+ while ((count = acl(path_p, ACL_GET, count, &acl_d->acl[0])) < 0 && errno == ENOSPC) {
+
+ sys_acl_free_acl(acl_d);
+
+ if ((count = acl(path_p, ACL_CNT, NACLENTRIES, NULL)) < 0) {
+ return NULL;
+ }
+
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+ }
+
+ if (count < 0) {
+ sys_acl_free_acl(acl_d);
+ return NULL;
+ }
+
+ /*
+ * calculate the number of access and default ACL entries
+ *
+ * Note: we assume that the acl() system call returned a
+ * well formed ACL which is sorted so that all of the
+ * access ACL entries preceed any default ACL entries
+ */
+ for (naccess = 0; naccess < count; naccess++) {
+ if (acl_d->acl[naccess].a_type & ACL_DEFAULT)
+ break;
+ }
+ ndefault = count - naccess;
+
+ /*
+ * if the caller wants the default ACL we have to copy
+ * the entries down to the start of the acl[] buffer
+ * and mask out the ACL_DEFAULT flag from the type field
+ */
+ if (type == SMB_ACL_TYPE_DEFAULT) {
+ int i, j;
+
+ for (i = 0, j = naccess; i < ndefault; i++, j++) {
+ acl_d->acl[i] = acl_d->acl[j];
+ acl_d->acl[i].a_type &= ~ACL_DEFAULT;
+ }
+
+ acl_d->count = ndefault;
+ } else {
+ acl_d->count = naccess;
+ }
+
+ return acl_d;
+}
+
+#if 0
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ /*
+ * HPUX doesn't have the facl call. Fake it using the path.... JRA.
+ */
+
+ files_struct *fsp = file_find_fd(fd);
+
+ if (fsp == NULL) {
+ errno = EBADF;
+ return NULL;
+ }
+
+ /*
+ * We know we're in the same conn context. So we
+ * can use the relative path.
+ */
+
+ return sys_acl_get_file(fsp->fsp_name, SMB_ACL_TYPE_ACCESS);
+}
+#endif
+
+int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *bits_p, id_t *u_g_id_p)
+{
+ *tag_type_p = entry->a_type;
+
+ *bits_p = entry->a_perm;
+
+ if (*tag_type_p == SMB_ACL_USER || *tag_type_p == SMB_ACL_GROUP)
+ *u_g_id_p = entry->a_id;
+
+ return 0;
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+ SMB_ACL_T a;
+
+ if (count < 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /*
+ * note that since the definition of the structure pointed
+ * to by the SMB_ACL_T includes the first element of the
+ * acl[] array, this actually allocates an ACL with room
+ * for (count+1) entries
+ */
+ if ((a = (SMB_ACL_T)SMB_MALLOC(sizeof a[0] + count * sizeof(struct acl))) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ a->size = count + 1;
+ a->count = 0;
+ a->next = -1;
+
+ return a;
+}
+
+
+int sys_acl_create_entry(SMB_ACL_T *acl_p, SMB_ACL_ENTRY_T *entry_p)
+{
+ SMB_ACL_T acl_d;
+ SMB_ACL_ENTRY_T entry_d;
+
+ if (acl_p == NULL || entry_p == NULL || (acl_d = *acl_p) == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->count >= acl_d->size) {
+ errno = ENOSPC;
+ return -1;
+ }
+
+ entry_d = &acl_d->acl[acl_d->count++];
+ entry_d->a_type = 0;
+ entry_d->a_id = -1;
+ entry_d->a_perm = 0;
+ *entry_p = entry_d;
+
+ return 0;
+}
+
+int sys_acl_set_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tag_type, uint32 bits, id_t u_g_id)
+{
+ entry->a_type = tag_type;
+
+ if (tag_type == SMB_ACL_USER || tag_type == SMB_ACL_GROUP)
+ entry->a_id = u_g_id;
+
+ entry->a_perm = bits;
+
+ return 0;
+}
+
+int sys_acl_set_access_bits(SMB_ACL_ENTRY_T entry_d, uint32 bits)
+{
+ entry_d->a_perm = bits;
+
+ return 0;
+}
+
+/* Structure to capture the count for each type of ACE. */
+
+struct hpux_acl_types {
+ int n_user;
+ int n_def_user;
+ int n_user_obj;
+ int n_def_user_obj;
+
+ int n_group;
+ int n_def_group;
+ int n_group_obj;
+ int n_def_group_obj;
+
+ int n_other;
+ int n_other_obj;
+ int n_def_other_obj;
+
+ int n_class_obj;
+ int n_def_class_obj;
+
+ int n_illegal_obj;
+};
+
+/* count_obj:
+ * Counts the different number of objects in a given array of ACL
+ * structures.
+ * Inputs:
+ *
+ * acl_count - Count of ACLs in the array of ACL strucutres.
+ * aclp - Array of ACL structures.
+ * acl_type_count - Pointer to acl_types structure. Should already be
+ * allocated.
+ * Output:
+ *
+ * acl_type_count - This structure is filled up with counts of various
+ * acl types.
+ */
+
+static void hpux_count_obj(int acl_count, struct acl *aclp, struct hpux_acl_types *acl_type_count)
+{
+ int i;
+
+ memset(acl_type_count, 0, sizeof(struct hpux_acl_types));
+
+ for(i=0;in_user++;
+ break;
+ case USER_OBJ:
+ acl_type_count->n_user_obj++;
+ break;
+ case DEF_USER_OBJ:
+ acl_type_count->n_def_user_obj++;
+ break;
+ case GROUP:
+ acl_type_count->n_group++;
+ break;
+ case GROUP_OBJ:
+ acl_type_count->n_group_obj++;
+ break;
+ case DEF_GROUP_OBJ:
+ acl_type_count->n_def_group_obj++;
+ break;
+ case OTHER_OBJ:
+ acl_type_count->n_other_obj++;
+ break;
+ case DEF_OTHER_OBJ:
+ acl_type_count->n_def_other_obj++;
+ break;
+ case CLASS_OBJ:
+ acl_type_count->n_class_obj++;
+ break;
+ case DEF_CLASS_OBJ:
+ acl_type_count->n_def_class_obj++;
+ break;
+ case DEF_USER:
+ acl_type_count->n_def_user++;
+ break;
+ case DEF_GROUP:
+ acl_type_count->n_def_group++;
+ break;
+ default:
+ acl_type_count->n_illegal_obj++;
+ break;
+ }
+ }
+}
+
+/* swap_acl_entries: Swaps two ACL entries.
+ *
+ * Inputs: aclp0, aclp1 - ACL entries to be swapped.
+ */
+
+static void hpux_swap_acl_entries(struct acl *aclp0, struct acl *aclp1)
+{
+ struct acl temp_acl;
+
+ temp_acl.a_type = aclp0->a_type;
+ temp_acl.a_id = aclp0->a_id;
+ temp_acl.a_perm = aclp0->a_perm;
+
+ aclp0->a_type = aclp1->a_type;
+ aclp0->a_id = aclp1->a_id;
+ aclp0->a_perm = aclp1->a_perm;
+
+ aclp1->a_type = temp_acl.a_type;
+ aclp1->a_id = temp_acl.a_id;
+ aclp1->a_perm = temp_acl.a_perm;
+}
+
+/* prohibited_duplicate_type
+ * Identifies if given ACL type can have duplicate entries or
+ * not.
+ *
+ * Inputs: acl_type - ACL Type.
+ *
+ * Outputs:
+ *
+ * Return..
+ *
+ * True - If the ACL type matches any of the prohibited types.
+ * False - If the ACL type doesn't match any of the prohibited types.
+ */
+
+static BOOL hpux_prohibited_duplicate_type(int acl_type)
+{
+ switch(acl_type) {
+ case USER:
+ case GROUP:
+ case DEF_USER:
+ case DEF_GROUP:
+ return True;
+ default:
+ return False;
+ }
+}
+
+/* get_needed_class_perm
+ * Returns the permissions of a ACL structure only if the ACL
+ * type matches one of the pre-determined types for computing
+ * CLASS_OBJ permissions.
+ *
+ * Inputs: aclp - Pointer to ACL structure.
+ */
+
+static int hpux_get_needed_class_perm(struct acl *aclp)
+{
+ switch(aclp->a_type) {
+ case USER:
+ case GROUP_OBJ:
+ case GROUP:
+ case DEF_USER_OBJ:
+ case DEF_USER:
+ case DEF_GROUP_OBJ:
+ case DEF_GROUP:
+ case DEF_CLASS_OBJ:
+ case DEF_OTHER_OBJ:
+ return aclp->a_perm;
+ default:
+ return 0;
+ }
+}
+
+/* acl_sort for HPUX.
+ * Sorts the array of ACL structures as per the description in
+ * aclsort man page. Refer to aclsort man page for more details
+ *
+ * Inputs:
+ *
+ * acl_count - Count of ACLs in the array of ACL structures.
+ * calclass - If this is not zero, then we compute the CLASS_OBJ
+ * permissions.
+ * aclp - Array of ACL structures.
+ *
+ * Outputs:
+ *
+ * aclp - Sorted array of ACL structures.
+ *
+ * Outputs:
+ *
+ * Returns 0 for success -1 for failure. Prints a message to the Samba
+ * debug log in case of failure.
+ */
+
+static int hpux_acl_sort(int acl_count, int calclass, struct acl *aclp)
+{
+#if !defined(HAVE_HPUX_ACLSORT)
+ /*
+ * The aclsort() system call is availabe on the latest HPUX General
+ * Patch Bundles. So for HPUX, we developed our version of acl_sort
+ * function. Because, we don't want to update to a new
+ * HPUX GR bundle just for aclsort() call.
+ */
+
+ struct hpux_acl_types acl_obj_count;
+ int n_class_obj_perm = 0;
+ int i, j;
+
+ if(!acl_count) {
+ DEBUG(10,("Zero acl count passed. Returning Success\n"));
+ return 0;
+ }
+
+ if(aclp == NULL) {
+ DEBUG(0,("Null ACL pointer in hpux_acl_sort. Returning Failure. \n"));
+ return -1;
+ }
+
+ /* Count different types of ACLs in the ACLs array */
+
+ hpux_count_obj(acl_count, aclp, &acl_obj_count);
+
+ /* There should be only one entry each of type USER_OBJ, GROUP_OBJ,
+ * CLASS_OBJ and OTHER_OBJ
+ */
+
+ if( (acl_obj_count.n_user_obj != 1) ||
+ (acl_obj_count.n_group_obj != 1) ||
+ (acl_obj_count.n_class_obj != 1) ||
+ (acl_obj_count.n_other_obj != 1)
+ ) {
+ DEBUG(0,("hpux_acl_sort: More than one entry or no entries for \
+USER OBJ or GROUP_OBJ or OTHER_OBJ or CLASS_OBJ\n"));
+ return -1;
+ }
+
+ /* If any of the default objects are present, there should be only
+ * one of them each.
+ */
+
+ if( (acl_obj_count.n_def_user_obj > 1) || (acl_obj_count.n_def_group_obj > 1) ||
+ (acl_obj_count.n_def_other_obj > 1) || (acl_obj_count.n_def_class_obj > 1) ) {
+ DEBUG(0,("hpux_acl_sort: More than one entry for DEF_CLASS_OBJ \
+or DEF_USER_OBJ or DEF_GROUP_OBJ or DEF_OTHER_OBJ\n"));
+ return -1;
+ }
+
+ /* We now have proper number of OBJ and DEF_OBJ entries. Now sort the acl
+ * structures.
+ *
+ * Sorting crieteria - First sort by ACL type. If there are multiple entries of
+ * same ACL type, sort by ACL id.
+ *
+ * I am using the trival kind of sorting method here because, performance isn't
+ * really effected by the ACLs feature. More over there aren't going to be more
+ * than 17 entries on HPUX.
+ */
+
+ for(i=0; i aclp[j].a_type ) {
+ /* ACL entries out of order, swap them */
+
+ hpux_swap_acl_entries((aclp+i), (aclp+j));
+
+ } else if ( aclp[i].a_type == aclp[j].a_type ) {
+
+ /* ACL entries of same type, sort by id */
+
+ if(aclp[i].a_id > aclp[j].a_id) {
+ hpux_swap_acl_entries((aclp+i), (aclp+j));
+ } else if (aclp[i].a_id == aclp[j].a_id) {
+ /* We have a duplicate entry. */
+ if(hpux_prohibited_duplicate_type(aclp[i].a_type)) {
+ DEBUG(0, ("hpux_acl_sort: Duplicate entry: Type(hex): %x Id: %d\n",
+ aclp[i].a_type, aclp[i].a_id));
+ return -1;
+ }
+ }
+
+ }
+ }
+ }
+
+ /* set the class obj permissions to the computed one. */
+ if(calclass) {
+ int n_class_obj_index = -1;
+
+ for(i=0;icount <= 4);
+
+ if (hpux_acl_sort(acl_d->count, fixmask, acl_d->acl) != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+int sys_acl_valid(SMB_ACL_T acl_d)
+{
+ return acl_sort(acl_d);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T type, SMB_ACL_T acl_d)
+{
+ struct stat s;
+ struct acl *acl_p;
+ int acl_count;
+ struct acl *acl_buf = NULL;
+ int ret;
+
+ if(hpux_acl_call_presence() == False) {
+ /* Looks like we don't have the acl() system call on HPUX.
+ * May be the system doesn't have the latest version of JFS.
+ */
+ errno=ENOSYS;
+ return -1;
+ }
+
+ if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_sort(acl_d) != 0) {
+ return -1;
+ }
+
+ acl_p = &acl_d->acl[0];
+ acl_count = acl_d->count;
+
+ /*
+ * if it's a directory there is extra work to do
+ * since the acl() system call will replace both
+ * the access ACLs and the default ACLs (if any)
+ */
+ if (stat(name, &s) != 0) {
+ return -1;
+ }
+ if (S_ISDIR(s.st_mode)) {
+ SMB_ACL_T acc_acl;
+ SMB_ACL_T def_acl;
+ SMB_ACL_T tmp_acl;
+ int i;
+
+ if (type == SMB_ACL_TYPE_ACCESS) {
+ acc_acl = acl_d;
+ def_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_DEFAULT);
+
+ } else {
+ def_acl = acl_d;
+ acc_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_ACCESS);
+ }
+
+ if (tmp_acl == NULL) {
+ return -1;
+ }
+
+ /*
+ * allocate a temporary buffer for the complete ACL
+ */
+ acl_count = acc_acl->count + def_acl->count;
+ acl_p = acl_buf = SMB_MALLOC_ARRAY(struct acl, acl_count);
+
+ if (acl_buf == NULL) {
+ sys_acl_free_acl(tmp_acl);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /*
+ * copy the access control and default entries into the buffer
+ */
+ memcpy(&acl_buf[0], &acc_acl->acl[0],
+ acc_acl->count * sizeof(acl_buf[0]));
+
+ memcpy(&acl_buf[acc_acl->count], &def_acl->acl[0],
+ def_acl->count * sizeof(acl_buf[0]));
+
+ /*
+ * set the ACL_DEFAULT flag on the default entries
+ */
+ for (i = acc_acl->count; i < acl_count; i++) {
+ acl_buf[i].a_type |= ACL_DEFAULT;
+ }
+
+ sys_acl_free_acl(tmp_acl);
+
+ } else if (type != SMB_ACL_TYPE_ACCESS) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ret = acl(name, ACL_SET, acl_count, acl_p);
+
+ if (acl_buf) {
+ free(acl_buf);
+ }
+
+ return ret;
+}
+
+#if 0
+int sys_acl_set_fd(int fd, SMB_ACL_T acl_d)
+{
+ /*
+ * HPUX doesn't have the facl call. Fake it using the path.... JRA.
+ */
+
+ files_struct *fsp = file_find_fd(fd);
+
+ if (fsp == NULL) {
+ errno = EBADF;
+ return NULL;
+ }
+
+ if (acl_sort(acl_d) != 0) {
+ return -1;
+ }
+
+ /*
+ * We know we're in the same conn context. So we
+ * can use the relative path.
+ */
+
+ return sys_acl_set_file(fsp->fsp_name, SMB_ACL_TYPE_ACCESS, acl_d);
+}
+#endif
+
+int sys_acl_delete_def_file(const char *path)
+{
+ SMB_ACL_T acl_d;
+ int ret;
+
+ /*
+ * fetching the access ACL and rewriting it has
+ * the effect of deleting the default ACL
+ */
+ if ((acl_d = sys_acl_get_file(path, SMB_ACL_TYPE_ACCESS)) == NULL) {
+ return -1;
+ }
+
+ ret = acl(path, ACL_SET, acl_d->count, acl_d->acl);
+
+ sys_acl_free_acl(acl_d);
+
+ return ret;
+}
+
+int sys_acl_free_acl(SMB_ACL_T acl_d)
+{
+ free(acl_d);
+ return 0;
+}
+
+#elif defined(HAVE_IRIX_ACLS) /*---------------------------------------------*/
+
+int sys_acl_get_entry(SMB_ACL_T acl_d, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ if (entry_id != SMB_ACL_FIRST_ENTRY && entry_id != SMB_ACL_NEXT_ENTRY) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_p == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_id == SMB_ACL_FIRST_ENTRY) {
+ acl_d->next = 0;
+ }
+
+ if (acl_d->next < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->next >= acl_d->aclp->acl_cnt) {
+ return 0;
+ }
+
+ *entry_p = &acl_d->aclp->acl_entry[acl_d->next++];
+
+ return 1;
+}
+
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *type_p)
+{
+ *type_p = entry_d->ae_tag;
+
+ return 0;
+}
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+ SMB_ACL_T a;
+
+ if ((a = SMB_MALLOC_P(struct SMB_ACL_T)) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ if ((a->aclp = acl_get_file(path_p, type)) == NULL) {
+ SAFE_FREE(a);
+ return NULL;
+ }
+ a->next = -1;
+ a->freeaclp = True;
+ return a;
+}
+
+#if 0
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ SMB_ACL_T a;
+
+ if ((a = SMB_MALLOC_P(struct SMB_ACL_T)) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ if ((a->aclp = acl_get_fd(fd)) == NULL) {
+ SAFE_FREE(a);
+ return NULL;
+ }
+ a->next = -1;
+ a->freeaclp = True;
+ return a;
+}
+#endif
+
+int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *bits_p, id_t *u_g_id_p)
+{
+ *tag_type_p = entry->ae_tag;
+
+ *bits_p = entry->ae_perm;
+
+ if (*tag_type_p == SMB_ACL_USER || *tag_type_p == SMB_ACL_GROUP)
+ *u_g_id_p = entry->ae_id;
+
+ return 0;
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+ SMB_ACL_T a;
+
+ if (count < 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if ((a = (SMB_ACL_T)SMB_MALLOC(sizeof a[0] + sizeof (struct acl))) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ a->next = -1;
+ a->freeaclp = False;
+ a->aclp = (struct acl *)((char *)a + sizeof a[0]);
+ a->aclp->acl_cnt = 0;
+
+ return a;
+}
+
+
+int sys_acl_create_entry(SMB_ACL_T *acl_p, SMB_ACL_ENTRY_T *entry_p)
+{
+ SMB_ACL_T acl_d;
+ SMB_ACL_ENTRY_T entry_d;
+
+ if (acl_p == NULL || entry_p == NULL || (acl_d = *acl_p) == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->aclp->acl_cnt >= ACL_MAX_ENTRIES) {
+ errno = ENOSPC;
+ return -1;
+ }
+
+ entry_d = &acl_d->aclp->acl_entry[acl_d->aclp->acl_cnt++];
+ entry_d->ae_tag = 0;
+ entry_d->ae_id = 0;
+ entry_d->ae_perm = 0;
+ *entry_p = entry_d;
+
+ return 0;
+}
+
+int sys_acl_set_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tag_type, uint32 bits, id_t u_g_id)
+{
+ entry->ae_tag = tag_type;
+
+ if (tag_type == SMB_ACL_USER || tag_type == SMB_ACL_GROUP)
+ entry->ae_id = u_g_id;
+
+ entry->ae_perm = bits;
+
+ return 0;
+}
+
+int sys_acl_set_access_bits(SMB_ACL_ENTRY_T entry_d, uint32 bits)
+{
+ entry_d->ae_perm = bits;
+
+ return 0;
+}
+
+int sys_acl_valid(SMB_ACL_T acl_d)
+{
+ return acl_valid(acl_d->aclp);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T type, SMB_ACL_T acl_d)
+{
+ return acl_set_file(name, type, acl_d->aclp);
+}
+
+#if 0
+int sys_acl_set_fd(int fd, SMB_ACL_T acl_d)
+{
+ return acl_set_fd(fd, acl_d->aclp);
+}
+#endif
+
+int sys_acl_delete_def_file(const char *name)
+{
+ return acl_delete_def_file(name);
+}
+
+int sys_acl_free_acl(SMB_ACL_T acl_d)
+{
+ if (acl_d->freeaclp) {
+ acl_free(acl_d->aclp);
+ }
+ acl_free(acl_d);
+ return 0;
+}
+
+#elif defined(HAVE_AIX_ACLS) /*----------------------------------------------*/
+
+/* Donated by Medha Date, mdate@austin.ibm.com, for IBM */
+
+int sys_acl_get_entry( SMB_ACL_T theacl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ struct acl_entry_link *link;
+ struct new_acl_entry *entry;
+ int keep_going;
+
+ if (entry_id == SMB_ACL_FIRST_ENTRY)
+ theacl->count = 0;
+ else if (entry_id != SMB_ACL_NEXT_ENTRY) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ DEBUG(10,("This is the count: %d\n",theacl->count));
+
+ /* Check if count was previously set to -1. *
+ * If it was, that means we reached the end *
+ * of the acl last time. */
+ if(theacl->count == -1)
+ return(0);
+
+ link = theacl;
+ /* To get to the next acl, traverse linked list until index *
+ * of acl matches the count we are keeping. This count is *
+ * incremented each time we return an acl entry. */
+
+ for(keep_going = 0; keep_going < theacl->count; keep_going++)
+ link = link->nextp;
+
+ entry = *entry_p = link->entryp;
+
+ DEBUG(10,("*entry_p is %d\n",entry_p));
+ DEBUG(10,("*entry_p->ace_access is %d\n",entry->ace_access));
+
+ /* Increment count */
+ theacl->count++;
+ if(link->nextp == NULL)
+ theacl->count = -1;
+
+ return(1);
+}
+
+int sys_acl_get_tag_type( SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+ /* Initialize tag type */
+
+ *tag_type_p = -1;
+ DEBUG(10,("the tagtype is %d\n",entry_d->ace_id->id_type));
+
+ /* Depending on what type of entry we have, *
+ * return tag type. */
+ switch(entry_d->ace_id->id_type) {
+ case ACEID_USER:
+ *tag_type_p = SMB_ACL_USER;
+ break;
+ case ACEID_GROUP:
+ *tag_type_p = SMB_ACL_GROUP;
+ break;
+
+ case SMB_ACL_USER_OBJ:
+ case SMB_ACL_GROUP_OBJ:
+ case SMB_ACL_OTHER:
+ *tag_type_p = entry_d->ace_id->id_type;
+ break;
+
+ default:
+ return(-1);
+ }
+
+ return(0);
+}
+
+SMB_ACL_T sys_acl_get_file( const char *path_p, SMB_ACL_TYPE_T type)
+{
+ struct acl *file_acl = (struct acl *)NULL;
+ struct acl_entry *acl_entry;
+ struct new_acl_entry *new_acl_entry;
+ struct ace_id *idp;
+ struct acl_entry_link *acl_entry_link;
+ struct acl_entry_link *acl_entry_link_head;
+ int i;
+ int rc = 0;
+
+ /* AIX has no DEFAULT */
+ if ( type == SMB_ACL_TYPE_DEFAULT ) {
+#ifdef ENOTSUP
+ errno = ENOTSUP;
+#else
+ errno = ENOSYS;
+#endif
+ return NULL;
+ }
+
+ /* Get the acl using statacl */
+
+ DEBUG(10,("Entering sys_acl_get_file\n"));
+ DEBUG(10,("path_p is %s\n",path_p));
+
+ file_acl = (struct acl *)SMB_MALLOC(BUFSIZ);
+
+ if(file_acl == NULL) {
+ errno=ENOMEM;
+ DEBUG(0,("Error in AIX sys_acl_get_file: %d\n",errno));
+ return(NULL);
+ }
+
+ memset(file_acl,0,BUFSIZ);
+
+ rc = statacl((char *)path_p,0,file_acl,BUFSIZ);
+ if(rc == -1) {
+ DEBUG(0,("statacl returned %d with errno %d\n",rc,errno));
+ SAFE_FREE(file_acl);
+ return(NULL);
+ }
+
+ DEBUG(10,("Got facl and returned it\n"));
+
+ /* Point to the first acl entry in the acl */
+ acl_entry = file_acl->acl_ext;
+
+ /* Begin setting up the head of the linked list *
+ * that will be used for the storing the acl *
+ * in a way that is useful for the posix_acls.c *
+ * code. */
+
+ acl_entry_link_head = acl_entry_link = sys_acl_init(0);
+ if(acl_entry_link_head == NULL)
+ return(NULL);
+
+ acl_entry_link->entryp = SMB_MALLOC_P(struct new_acl_entry);
+ if(acl_entry_link->entryp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno));
+ return(NULL);
+ }
+
+ DEBUG(10,("acl_entry is %d\n",acl_entry));
+ DEBUG(10,("acl_last(file_acl) id %d\n",acl_last(file_acl)));
+
+ /* Check if the extended acl bit is on. *
+ * If it isn't, do not show the *
+ * contents of the acl since AIX intends *
+ * the extended info to remain unused */
+
+ if(file_acl->acl_mode & S_IXACL){
+ /* while we are not pointing to the very end */
+ while(acl_entry < acl_last(file_acl)) {
+ /* before we malloc anything, make sure this is */
+ /* a valid acl entry and one that we want to map */
+ idp = id_nxt(acl_entry->ace_id);
+ if((acl_entry->ace_type == ACC_SPECIFY ||
+ (acl_entry->ace_type == ACC_PERMIT)) && (idp != id_last(acl_entry))) {
+ acl_entry = acl_nxt(acl_entry);
+ continue;
+ }
+
+ idp = acl_entry->ace_id;
+
+ /* Check if this is the first entry in the linked list. *
+ * The first entry needs to keep prevp pointing to NULL *
+ * and already has entryp allocated. */
+
+ if(acl_entry_link_head->count != 0) {
+ acl_entry_link->nextp = SMB_MALLOC_P(struct acl_entry_link);
+
+ if(acl_entry_link->nextp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno));
+ return(NULL);
+ }
+
+ acl_entry_link->nextp->prevp = acl_entry_link;
+ acl_entry_link = acl_entry_link->nextp;
+ acl_entry_link->entryp = SMB_MALLOC_P(struct new_acl_entry);
+ if(acl_entry_link->entryp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno));
+ return(NULL);
+ }
+ acl_entry_link->nextp = NULL;
+ }
+
+ acl_entry_link->entryp->ace_len = acl_entry->ace_len;
+
+ /* Don't really need this since all types are going *
+ * to be specified but, it's better than leaving it 0 */
+
+ acl_entry_link->entryp->ace_type = acl_entry->ace_type;
+
+ acl_entry_link->entryp->ace_access = acl_entry->ace_access;
+
+ memcpy(acl_entry_link->entryp->ace_id,idp,sizeof(struct ace_id));
+
+ /* The access in the acl entries must be left shifted by *
+ * three bites, because they will ultimately be compared *
+ * to S_IRUSR, S_IWUSR, and S_IXUSR. */
+
+ switch(acl_entry->ace_type){
+ case ACC_PERMIT:
+ case ACC_SPECIFY:
+ acl_entry_link->entryp->ace_access = acl_entry->ace_access;
+ acl_entry_link->entryp->ace_access <<= 6;
+ acl_entry_link_head->count++;
+ break;
+ case ACC_DENY:
+ /* Since there is no way to return a DENY acl entry *
+ * change to PERMIT and then shift. */
+ DEBUG(10,("acl_entry->ace_access is %d\n",acl_entry->ace_access));
+ acl_entry_link->entryp->ace_access = ~acl_entry->ace_access & 7;
+ DEBUG(10,("acl_entry_link->entryp->ace_access is %d\n",acl_entry_link->entryp->ace_access));
+ acl_entry_link->entryp->ace_access <<= 6;
+ acl_entry_link_head->count++;
+ break;
+ default:
+ return(0);
+ }
+
+ DEBUG(10,("acl_entry = %d\n",acl_entry));
+ DEBUG(10,("The ace_type is %d\n",acl_entry->ace_type));
+
+ acl_entry = acl_nxt(acl_entry);
+ }
+ } /* end of if enabled */
+
+ /* Since owner, group, other acl entries are not *
+ * part of the acl entries in an acl, they must *
+ * be dummied up to become part of the list. */
+
+ for( i = 1; i < 4; i++) {
+ DEBUG(10,("i is %d\n",i));
+ if(acl_entry_link_head->count != 0) {
+ acl_entry_link->nextp = SMB_MALLOC_P(struct acl_entry_link);
+ if(acl_entry_link->nextp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno));
+ return(NULL);
+ }
+
+ acl_entry_link->nextp->prevp = acl_entry_link;
+ acl_entry_link = acl_entry_link->nextp;
+ acl_entry_link->entryp = SMB_MALLOC_P(struct new_acl_entry);
+ if(acl_entry_link->entryp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno));
+ return(NULL);
+ }
+ }
+
+ acl_entry_link->nextp = NULL;
+
+ new_acl_entry = acl_entry_link->entryp;
+ idp = new_acl_entry->ace_id;
+
+ new_acl_entry->ace_len = sizeof(struct acl_entry);
+ new_acl_entry->ace_type = ACC_PERMIT;
+ idp->id_len = sizeof(struct ace_id);
+ DEBUG(10,("idp->id_len = %d\n",idp->id_len));
+ memset(idp->id_data,0,sizeof(uid_t));
+
+ switch(i) {
+ case 2:
+ new_acl_entry->ace_access = file_acl->g_access << 6;
+ idp->id_type = SMB_ACL_GROUP_OBJ;
+ break;
+
+ case 3:
+ new_acl_entry->ace_access = file_acl->o_access << 6;
+ idp->id_type = SMB_ACL_OTHER;
+ break;
+
+ case 1:
+ new_acl_entry->ace_access = file_acl->u_access << 6;
+ idp->id_type = SMB_ACL_USER_OBJ;
+ break;
+
+ default:
+ return(NULL);
+
+ }
+
+ acl_entry_link_head->count++;
+ DEBUG(10,("new_acl_entry->ace_access = %d\n",new_acl_entry->ace_access));
+ }
+
+ acl_entry_link_head->count = 0;
+ SAFE_FREE(file_acl);
+
+ return(acl_entry_link_head);
+}
+
+#if 0
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ struct acl *file_acl = (struct acl *)NULL;
+ struct acl_entry *acl_entry;
+ struct new_acl_entry *new_acl_entry;
+ struct ace_id *idp;
+ struct acl_entry_link *acl_entry_link;
+ struct acl_entry_link *acl_entry_link_head;
+ int i;
+ int rc = 0;
+
+ /* Get the acl using fstatacl */
+
+ DEBUG(10,("Entering sys_acl_get_fd\n"));
+ DEBUG(10,("fd is %d\n",fd));
+ file_acl = (struct acl *)SMB_MALLOC(BUFSIZ);
+
+ if(file_acl == NULL) {
+ errno=ENOMEM;
+ DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno));
+ return(NULL);
+ }
+
+ memset(file_acl,0,BUFSIZ);
+
+ rc = fstatacl(fd,0,file_acl,BUFSIZ);
+ if(rc == -1) {
+ DEBUG(0,("The fstatacl call returned %d with errno %d\n",rc,errno));
+ SAFE_FREE(file_acl);
+ return(NULL);
+ }
+
+ DEBUG(10,("Got facl and returned it\n"));
+
+ /* Point to the first acl entry in the acl */
+
+ acl_entry = file_acl->acl_ext;
+ /* Begin setting up the head of the linked list *
+ * that will be used for the storing the acl *
+ * in a way that is useful for the posix_acls.c *
+ * code. */
+
+ acl_entry_link_head = acl_entry_link = sys_acl_init(0);
+ if(acl_entry_link_head == NULL){
+ SAFE_FREE(file_acl);
+ return(NULL);
+ }
+
+ acl_entry_link->entryp = SMB_MALLOC_P(struct new_acl_entry);
+
+ if(acl_entry_link->entryp == NULL) {
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno));
+ SAFE_FREE(file_acl);
+ return(NULL);
+ }
+
+ DEBUG(10,("acl_entry is %d\n",acl_entry));
+ DEBUG(10,("acl_last(file_acl) id %d\n",acl_last(file_acl)));
+
+ /* Check if the extended acl bit is on. *
+ * If it isn't, do not show the *
+ * contents of the acl since AIX intends *
+ * the extended info to remain unused */
+
+ if(file_acl->acl_mode & S_IXACL){
+ /* while we are not pointing to the very end */
+ while(acl_entry < acl_last(file_acl)) {
+ /* before we malloc anything, make sure this is */
+ /* a valid acl entry and one that we want to map */
+
+ idp = id_nxt(acl_entry->ace_id);
+ if((acl_entry->ace_type == ACC_SPECIFY ||
+ (acl_entry->ace_type == ACC_PERMIT)) && (idp != id_last(acl_entry))) {
+ acl_entry = acl_nxt(acl_entry);
+ continue;
+ }
+
+ idp = acl_entry->ace_id;
+
+ /* Check if this is the first entry in the linked list. *
+ * The first entry needs to keep prevp pointing to NULL *
+ * and already has entryp allocated. */
+
+ if(acl_entry_link_head->count != 0) {
+ acl_entry_link->nextp = SMB_MALLOC_P(struct acl_entry_link);
+ if(acl_entry_link->nextp == NULL) {
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno));
+ SAFE_FREE(file_acl);
+ return(NULL);
+ }
+ acl_entry_link->nextp->prevp = acl_entry_link;
+ acl_entry_link = acl_entry_link->nextp;
+ acl_entry_link->entryp = SMB_MALLOC_P(struct new_acl_entry);
+ if(acl_entry_link->entryp == NULL) {
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno));
+ SAFE_FREE(file_acl);
+ return(NULL);
+ }
+
+ acl_entry_link->nextp = NULL;
+ }
+
+ acl_entry_link->entryp->ace_len = acl_entry->ace_len;
+
+ /* Don't really need this since all types are going *
+ * to be specified but, it's better than leaving it 0 */
+
+ acl_entry_link->entryp->ace_type = acl_entry->ace_type;
+ acl_entry_link->entryp->ace_access = acl_entry->ace_access;
+
+ memcpy(acl_entry_link->entryp->ace_id, idp, sizeof(struct ace_id));
+
+ /* The access in the acl entries must be left shifted by *
+ * three bites, because they will ultimately be compared *
+ * to S_IRUSR, S_IWUSR, and S_IXUSR. */
+
+ switch(acl_entry->ace_type){
+ case ACC_PERMIT:
+ case ACC_SPECIFY:
+ acl_entry_link->entryp->ace_access = acl_entry->ace_access;
+ acl_entry_link->entryp->ace_access <<= 6;
+ acl_entry_link_head->count++;
+ break;
+ case ACC_DENY:
+ /* Since there is no way to return a DENY acl entry *
+ * change to PERMIT and then shift. */
+ DEBUG(10,("acl_entry->ace_access is %d\n",acl_entry->ace_access));
+ acl_entry_link->entryp->ace_access = ~acl_entry->ace_access & 7;
+ DEBUG(10,("acl_entry_link->entryp->ace_access is %d\n",acl_entry_link->entryp->ace_access));
+ acl_entry_link->entryp->ace_access <<= 6;
+ acl_entry_link_head->count++;
+ break;
+ default:
+ return(0);
+ }
+
+ DEBUG(10,("acl_entry = %d\n",acl_entry));
+ DEBUG(10,("The ace_type is %d\n",acl_entry->ace_type));
+
+ acl_entry = acl_nxt(acl_entry);
+ }
+ } /* end of if enabled */
+
+ /* Since owner, group, other acl entries are not *
+ * part of the acl entries in an acl, they must *
+ * be dummied up to become part of the list. */
+
+ for( i = 1; i < 4; i++) {
+ DEBUG(10,("i is %d\n",i));
+ if(acl_entry_link_head->count != 0){
+ acl_entry_link->nextp = SMB_MALLOC_P(struct acl_entry_link);
+ if(acl_entry_link->nextp == NULL) {
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno));
+ SAFE_FREE(file_acl);
+ return(NULL);
+ }
+
+ acl_entry_link->nextp->prevp = acl_entry_link;
+ acl_entry_link = acl_entry_link->nextp;
+ acl_entry_link->entryp = SMB_MALLOC_P(struct new_acl_entry);
+
+ if(acl_entry_link->entryp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno));
+ return(NULL);
+ }
+ }
+
+ acl_entry_link->nextp = NULL;
+
+ new_acl_entry = acl_entry_link->entryp;
+ idp = new_acl_entry->ace_id;
+
+ new_acl_entry->ace_len = sizeof(struct acl_entry);
+ new_acl_entry->ace_type = ACC_PERMIT;
+ idp->id_len = sizeof(struct ace_id);
+ DEBUG(10,("idp->id_len = %d\n",idp->id_len));
+ memset(idp->id_data,0,sizeof(uid_t));
+
+ switch(i) {
+ case 2:
+ new_acl_entry->ace_access = file_acl->g_access << 6;
+ idp->id_type = SMB_ACL_GROUP_OBJ;
+ break;
+
+ case 3:
+ new_acl_entry->ace_access = file_acl->o_access << 6;
+ idp->id_type = SMB_ACL_OTHER;
+ break;
+
+ case 1:
+ new_acl_entry->ace_access = file_acl->u_access << 6;
+ idp->id_type = SMB_ACL_USER_OBJ;
+ break;
+
+ default:
+ return(NULL);
+ }
+
+ acl_entry_link_head->count++;
+ DEBUG(10,("new_acl_entry->ace_access = %d\n",new_acl_entry->ace_access));
+ }
+
+ acl_entry_link_head->count = 0;
+ SAFE_FREE(file_acl);
+
+ return(acl_entry_link_head);
+}
+#endif
+
+int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *bits_p, id_t *u_g_id_p)
+{
+ uint *permset;
+
+ if (sys_acl_get_tag_type(entry, tag_type_p) != 0)
+ return -1;
+
+ if (*tag_type_p == SMB_ACL_USER || *tag_type_p == SMB_ACL_GROUP)
+ memcpy(u_g_id_p, entry->ace_id->id_data, sizeof (id_t));
+
+ permset = &entry->ace_access;
+
+ DEBUG(10,("*permset is %d\n",*permset));
+ *bits_p = (*permset & S_IRUSR ? 4 : 0)
+ | (*permset & S_IWUSR ? 2 : 0)
+ | (*permset & S_IXUSR ? 1 : 0);
+
+ return 0;
+}
+
+SMB_ACL_T sys_acl_init( int count)
+{
+ struct acl_entry_link *theacl = NULL;
+
+ if (count < 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ DEBUG(10,("Entering sys_acl_init\n"));
+
+ theacl = SMB_MALLOC_P(struct acl_entry_link);
+ if(theacl == NULL) {
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_init is %d\n",errno));
+ return(NULL);
+ }
+
+ theacl->count = 0;
+ theacl->nextp = NULL;
+ theacl->prevp = NULL;
+ theacl->entryp = NULL;
+ DEBUG(10,("Exiting sys_acl_init\n"));
+ return(theacl);
+}
+
+int sys_acl_create_entry( SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+ struct acl_entry_link *theacl;
+ struct acl_entry_link *acl_entryp;
+ struct acl_entry_link *temp_entry;
+ int counting;
+
+ DEBUG(10,("Entering the sys_acl_create_entry\n"));
+
+ theacl = acl_entryp = *pacl;
+
+ /* Get to the end of the acl before adding entry */
+
+ for(counting=0; counting < theacl->count; counting++){
+ DEBUG(10,("The acl_entryp is %d\n",acl_entryp));
+ temp_entry = acl_entryp;
+ acl_entryp = acl_entryp->nextp;
+ }
+
+ if(theacl->count != 0){
+ temp_entry->nextp = acl_entryp = SMB_MALLOC_P(struct acl_entry_link);
+ if(acl_entryp == NULL) {
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_create_entry is %d\n",errno));
+ return(-1);
+ }
+
+ DEBUG(10,("The acl_entryp is %d\n",acl_entryp));
+ acl_entryp->prevp = temp_entry;
+ DEBUG(10,("The acl_entryp->prevp is %d\n",acl_entryp->prevp));
+ }
+
+ *pentry = acl_entryp->entryp = SMB_MALLOC_P(struct new_acl_entry);
+ if(*pentry == NULL) {
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_create_entry is %d\n",errno));
+ return(-1);
+ }
+
+ memset(*pentry,0,sizeof(struct new_acl_entry));
+ acl_entryp->entryp->ace_len = sizeof(struct acl_entry);
+ acl_entryp->entryp->ace_type = ACC_PERMIT;
+ acl_entryp->entryp->ace_id->id_len = sizeof(struct ace_id);
+ acl_entryp->nextp = NULL;
+ theacl->count++;
+ DEBUG(10,("Exiting sys_acl_create_entry\n"));
+ return(0);
+}
+
+int sys_acl_set_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tag_type, uint32 bits, id_t u_g_id)
+{
+ entry->ace_id->id_type = tag_type;
+ DEBUG(10,("The tag type is %d\n",entry->ace_id->id_type));
+
+ if (tag_type == SMB_ACL_USER || tag_type == SMB_ACL_GROUP)
+ memcpy(entry->ace_id->id_data, &u_g_id, sizeof (id_t));
+
+ entry->ace_access = bits;
+ DEBUG(10,("entry->ace_access = %d\n",entry->ace_access));
+
+ return 0;
+}
+
+int sys_acl_set_access_bits(SMB_ACL_ENTRY_T entry, uint32 bits)
+{
+ DEBUG(10,("Starting AIX sys_acl_set_permset\n"));
+ entry->ace_access = bits;
+ DEBUG(10,("entry->ace_access = %d\n",entry->ace_access));
+ DEBUG(10,("Ending AIX sys_acl_set_permset\n"));
+ return(0);
+}
+
+int sys_acl_valid( SMB_ACL_T theacl )
+{
+ int user_obj = 0;
+ int group_obj = 0;
+ int other_obj = 0;
+ struct acl_entry_link *acl_entry;
+
+ for(acl_entry=theacl; acl_entry != NULL; acl_entry = acl_entry->nextp) {
+ user_obj += (acl_entry->entryp->ace_id->id_type == SMB_ACL_USER_OBJ);
+ group_obj += (acl_entry->entryp->ace_id->id_type == SMB_ACL_GROUP_OBJ);
+ other_obj += (acl_entry->entryp->ace_id->id_type == SMB_ACL_OTHER);
+ }
+
+ DEBUG(10,("user_obj=%d, group_obj=%d, other_obj=%d\n",user_obj,group_obj,other_obj));
+
+ if(user_obj != 1 || group_obj != 1 || other_obj != 1)
+ return(-1);
+
+ return(0);
+}
+
+int sys_acl_set_file( const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+ struct acl_entry_link *acl_entry_link = NULL;
+ struct acl *file_acl = NULL;
+ struct acl *file_acl_temp = NULL;
+ struct acl_entry *acl_entry = NULL;
+ struct ace_id *ace_id = NULL;
+ uint id_type;
+ uint user_id;
+ uint acl_length;
+ uint rc;
+
+ DEBUG(10,("Entering sys_acl_set_file\n"));
+ DEBUG(10,("File name is %s\n",name));
+
+ /* AIX has no default ACL */
+ if(acltype == SMB_ACL_TYPE_DEFAULT)
+ return(0);
+
+ acl_length = BUFSIZ;
+ file_acl = (struct acl *)SMB_MALLOC(BUFSIZ);
+
+ if(file_acl == NULL) {
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_set_file is %d\n",errno));
+ return(-1);
+ }
+
+ memset(file_acl,0,BUFSIZ);
+
+ file_acl->acl_len = ACL_SIZ;
+ file_acl->acl_mode = S_IXACL;
+
+ for(acl_entry_link=theacl; acl_entry_link != NULL; acl_entry_link = acl_entry_link->nextp) {
+ acl_entry_link->entryp->ace_access >>= 6;
+ id_type = acl_entry_link->entryp->ace_id->id_type;
+
+ switch(id_type) {
+ case SMB_ACL_USER_OBJ:
+ file_acl->u_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_GROUP_OBJ:
+ file_acl->g_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_OTHER:
+ file_acl->o_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_MASK:
+ continue;
+ }
+
+ if((file_acl->acl_len + sizeof(struct acl_entry)) > acl_length) {
+ acl_length += sizeof(struct acl_entry);
+ file_acl_temp = (struct acl *)SMB_MALLOC(acl_length);
+ if(file_acl_temp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_set_file is %d\n",errno));
+ return(-1);
+ }
+
+ memcpy(file_acl_temp,file_acl,file_acl->acl_len);
+ SAFE_FREE(file_acl);
+ file_acl = file_acl_temp;
+ }
+
+ acl_entry = (struct acl_entry *)((char *)file_acl + file_acl->acl_len);
+ file_acl->acl_len += sizeof(struct acl_entry);
+ acl_entry->ace_len = acl_entry_link->entryp->ace_len;
+ acl_entry->ace_access = acl_entry_link->entryp->ace_access;
+
+ /* In order to use this, we'll need to wait until we can get denies */
+ /* if(!acl_entry->ace_access && acl_entry->ace_type == ACC_PERMIT)
+ acl_entry->ace_type = ACC_SPECIFY; */
+
+ acl_entry->ace_type = ACC_SPECIFY;
+
+ ace_id = acl_entry->ace_id;
+
+ ace_id->id_type = acl_entry_link->entryp->ace_id->id_type;
+ DEBUG(10,("The id type is %d\n",ace_id->id_type));
+ ace_id->id_len = acl_entry_link->entryp->ace_id->id_len;
+ memcpy(&user_id, acl_entry_link->entryp->ace_id->id_data, sizeof(uid_t));
+ memcpy(acl_entry->ace_id->id_data, &user_id, sizeof(uid_t));
+ }
+
+ rc = chacl((char*)name,file_acl,file_acl->acl_len);
+ DEBUG(10,("errno is %d\n",errno));
+ DEBUG(10,("return code is %d\n",rc));
+ SAFE_FREE(file_acl);
+ DEBUG(10,("Exiting the sys_acl_set_file\n"));
+ return(rc);
+}
+
+#if 0
+int sys_acl_set_fd( int fd, SMB_ACL_T theacl)
+{
+ struct acl_entry_link *acl_entry_link = NULL;
+ struct acl *file_acl = NULL;
+ struct acl *file_acl_temp = NULL;
+ struct acl_entry *acl_entry = NULL;
+ struct ace_id *ace_id = NULL;
+ uint id_type;
+ uint user_id;
+ uint acl_length;
+ uint rc;
+
+ DEBUG(10,("Entering sys_acl_set_fd\n"));
+ acl_length = BUFSIZ;
+ file_acl = (struct acl *)SMB_MALLOC(BUFSIZ);
+
+ if(file_acl == NULL) {
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_set_fd is %d\n",errno));
+ return(-1);
+ }
+
+ memset(file_acl,0,BUFSIZ);
+
+ file_acl->acl_len = ACL_SIZ;
+ file_acl->acl_mode = S_IXACL;
+
+ for(acl_entry_link=theacl; acl_entry_link != NULL; acl_entry_link = acl_entry_link->nextp) {
+ acl_entry_link->entryp->ace_access >>= 6;
+ id_type = acl_entry_link->entryp->ace_id->id_type;
+ DEBUG(10,("The id_type is %d\n",id_type));
+
+ switch(id_type) {
+ case SMB_ACL_USER_OBJ:
+ file_acl->u_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_GROUP_OBJ:
+ file_acl->g_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_OTHER:
+ file_acl->o_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_MASK:
+ continue;
+ }
+
+ if((file_acl->acl_len + sizeof(struct acl_entry)) > acl_length) {
+ acl_length += sizeof(struct acl_entry);
+ file_acl_temp = (struct acl *)SMB_MALLOC(acl_length);
+ if(file_acl_temp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_set_fd is %d\n",errno));
+ return(-1);
+ }
+
+ memcpy(file_acl_temp,file_acl,file_acl->acl_len);
+ SAFE_FREE(file_acl);
+ file_acl = file_acl_temp;
+ }
+
+ acl_entry = (struct acl_entry *)((char *)file_acl + file_acl->acl_len);
+ file_acl->acl_len += sizeof(struct acl_entry);
+ acl_entry->ace_len = acl_entry_link->entryp->ace_len;
+ acl_entry->ace_access = acl_entry_link->entryp->ace_access;
+
+ /* In order to use this, we'll need to wait until we can get denies */
+ /* if(!acl_entry->ace_access && acl_entry->ace_type == ACC_PERMIT)
+ acl_entry->ace_type = ACC_SPECIFY; */
+
+ acl_entry->ace_type = ACC_SPECIFY;
+
+ ace_id = acl_entry->ace_id;
+
+ ace_id->id_type = acl_entry_link->entryp->ace_id->id_type;
+ DEBUG(10,("The id type is %d\n",ace_id->id_type));
+ ace_id->id_len = acl_entry_link->entryp->ace_id->id_len;
+ memcpy(&user_id, acl_entry_link->entryp->ace_id->id_data, sizeof(uid_t));
+ memcpy(ace_id->id_data, &user_id, sizeof(uid_t));
+ }
+
+ rc = fchacl(fd,file_acl,file_acl->acl_len);
+ DEBUG(10,("errno is %d\n",errno));
+ DEBUG(10,("return code is %d\n",rc));
+ SAFE_FREE(file_acl);
+ DEBUG(10,("Exiting sys_acl_set_fd\n"));
+ return(rc);
+}
+#endif
+
+int sys_acl_delete_def_file(UNUSED(const char *name))
+{
+ /* AIX has no default ACL */
+ return 0;
+}
+
+int sys_acl_free_acl(SMB_ACL_T posix_acl)
+{
+ struct acl_entry_link *acl_entry_link;
+
+ for(acl_entry_link = posix_acl->nextp; acl_entry_link->nextp != NULL; acl_entry_link = acl_entry_link->nextp) {
+ SAFE_FREE(acl_entry_link->prevp->entryp);
+ SAFE_FREE(acl_entry_link->prevp);
+ }
+
+ SAFE_FREE(acl_entry_link->prevp->entryp);
+ SAFE_FREE(acl_entry_link->prevp);
+ SAFE_FREE(acl_entry_link->entryp);
+ SAFE_FREE(acl_entry_link);
+
+ return(0);
+}
+
+#elif defined(HAVE_OSX_ACLS) /*----------------------------------------------*/
+
+#define OSX_BROKEN_GETENTRY /* returns 0 instead of 1 */
+
+#include
+
+int sys_acl_get_entry(SMB_ACL_T the_acl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ int ret = acl_get_entry(the_acl, entry_id, entry_p);
+#ifdef OSX_BROKEN_GETENTRY
+ if (ret == 0)
+ ret = 1;
+ else if (ret == -1 && errno == 22)
+ ret = 0;
+#endif
+ return ret;
+}
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+ if (type == ACL_TYPE_DEFAULT) {
+ errno = ENOTSUP;
+ return NULL;
+ }
+ errno = 0;
+ return acl_get_file(path_p, type);
+}
+
+#if 0
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ return acl_get_fd(fd);
+}
+#endif
+
+int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *bits_p, id_t *u_g_id_p)
+{
+ uuid_t *uup;
+ acl_tag_t tag;
+ acl_flagset_t flagset;
+ acl_permset_t permset;
+ uint32 bits, fb, bb, pb;
+ int id_type = -1;
+ int rc;
+
+ if (acl_get_tag_type(entry, &tag) != 0
+ || acl_get_flagset_np(entry, &flagset) != 0
+ || acl_get_permset(entry, &permset) != 0
+ || (uup = acl_get_qualifier(entry)) == NULL)
+ return -1;
+
+ rc = mbr_uuid_to_id(*uup, u_g_id_p, &id_type);
+ acl_free(uup);
+ if (rc != 0)
+ return rc;
+
+ if (id_type == ID_TYPE_UID)
+ *tag_type_p = SMB_ACL_USER;
+ else
+ *tag_type_p = SMB_ACL_GROUP;
+
+ bits = tag == ACL_EXTENDED_ALLOW ? 1 : 0;
+
+ for (fb = (1u<<4), bb = (1u<<1); bb < (1u<<12); fb *= 2, bb *= 2) {
+ if (acl_get_flag_np(flagset, fb) == 1)
+ bits |= bb;
+ }
+
+ for (pb = (1u<<1), bb = (1u<<12); bb < (1u<<25); pb *= 2, bb *= 2) {
+ if (acl_get_perm_np(permset, pb) == 1)
+ bits |= bb;
+ }
+
+ *bits_p = bits;
+
+ return 0;
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+ return acl_init(count);
+}
+
+int sys_acl_create_entry(SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+ return acl_create_entry(pacl, pentry);
+}
+
+int sys_acl_set_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tag_type, uint32 bits, id_t u_g_id)
+{
+ acl_flagset_t flagset;
+ acl_permset_t permset;
+ uint32 fb, bb, pb;
+ int is_user = tag_type == SMB_ACL_USER;
+ uuid_t uu;
+ int rc;
+
+ tag_type = bits & 1 ? ACL_EXTENDED_ALLOW : ACL_EXTENDED_DENY;
+
+ if (acl_get_flagset_np(entry, &flagset) != 0
+ || acl_get_permset(entry, &permset) != 0)
+ return -1;
+
+ acl_clear_flags_np(flagset);
+ acl_clear_perms(permset);
+
+ for (fb = (1u<<4), bb = (1u<<1); bb < (1u<<12); fb *= 2, bb *= 2) {
+ if (bits & bb)
+ acl_add_flag_np(flagset, fb);
+ }
+
+ for (pb = (1u<<1), bb = (1u<<12); bb < (1u<<25); pb *= 2, bb *= 2) {
+ if (bits & bb)
+ acl_add_perm(permset, pb);
+ }
+
+ if (is_user)
+ rc = mbr_uid_to_uuid(u_g_id, uu);
+ else
+ rc = mbr_gid_to_uuid(u_g_id, uu);
+ if (rc != 0)
+ return rc;
+
+ if (acl_set_tag_type(entry, tag_type) != 0
+ || acl_set_qualifier(entry, &uu) != 0
+ || acl_set_permset(entry, permset) != 0
+ || acl_set_flagset_np(entry, flagset) != 0)
+ return -1;
+
+ return 0;
+}
+
+#if 0
+int sys_acl_set_access_bits(SMB_ACL_ENTRY_T entry, uint32 bits)
+{
+ return -1; /* Not needed for OS X. */
+}
+#endif
+
+int sys_acl_valid(SMB_ACL_T theacl)
+{
+ return acl_valid(theacl);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+ return acl_set_file(name, acltype, theacl);
+}
+
+#if 0
+int sys_acl_set_fd(int fd, SMB_ACL_T theacl)
+{
+ return acl_set_fd(fd, theacl);
+}
+#endif
+
+int sys_acl_delete_def_file(const char *name)
+{
+ return acl_delete_def_file(name);
+}
+
+int sys_acl_free_acl(SMB_ACL_T the_acl)
+{
+ return acl_free(the_acl);
+}
+
+#else /* No ACLs. */
+
+#error No ACL functions defined for this platform!
+
+#endif
+
+/************************************************************************
+ Deliberately outside the ACL defines. Return 1 if this is a "no acls"
+ errno, 0 if not.
+************************************************************************/
+
+int no_acl_syscall_error(int err)
+{
+#ifdef HAVE_OSX_ACLS
+ if (err == ENOENT)
+ return 1; /* Weird problem with directory ACLs. */
+#endif
+#if defined(ENOSYS)
+ if (err == ENOSYS) {
+ return 1;
+ }
+#endif
+#if defined(ENOTSUP)
+ if (err == ENOTSUP) {
+ return 1;
+ }
+#endif
+ if (err == EINVAL) {
+ /* If the type of SMB_ACL_TYPE_ACCESS or SMB_ACL_TYPE_DEFAULT
+ * isn't valid, then the ACLs must be non-POSIX. */
+ return 1;
+ }
+ return 0;
+}
+
+#endif /* SUPPORT_ACLS */
diff --git a/rsync/lib/sysacls.h b/rsync/lib/sysacls.h
new file mode 100644
index 0000000..31c4909
--- /dev/null
+++ b/rsync/lib/sysacls.h
@@ -0,0 +1,305 @@
+/*
+ * Unix SMB/Netbios implementation.
+ * Version 2.2.x
+ * Portable SMB ACL interface
+ * Copyright (C) Jeremy Allison 2000
+ * Copyright (C) 2007-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#ifdef SUPPORT_ACLS
+
+#ifdef HAVE_SYS_ACL_H
+#include
+#endif
+#ifdef HAVE_ACL_LIBACL_H
+#include
+#endif
+
+#define SMB_MALLOC(cnt) new_array(char, cnt)
+#define SMB_MALLOC_P(obj) new_array(obj, 1)
+#define SMB_MALLOC_ARRAY(obj, cnt) new_array(obj, cnt)
+#define SMB_REALLOC(mem, cnt) realloc_array(mem, char, cnt)
+#define slprintf snprintf
+
+#if defined HAVE_POSIX_ACLS /*-----------------------------------------------*/
+
+/* This is an identity mapping (just remove the SMB_). */
+
+#define SMB_ACL_TAG_T acl_tag_t
+#define SMB_ACL_TYPE_T acl_type_t
+
+/* Types of ACLs. */
+#define SMB_ACL_USER ACL_USER
+#define SMB_ACL_USER_OBJ ACL_USER_OBJ
+#define SMB_ACL_GROUP ACL_GROUP
+#define SMB_ACL_GROUP_OBJ ACL_GROUP_OBJ
+#define SMB_ACL_OTHER ACL_OTHER
+#define SMB_ACL_MASK ACL_MASK
+
+#define SMB_ACL_T acl_t
+
+#define SMB_ACL_ENTRY_T acl_entry_t
+
+#define SMB_ACL_FIRST_ENTRY ACL_FIRST_ENTRY
+#define SMB_ACL_NEXT_ENTRY ACL_NEXT_ENTRY
+
+#define SMB_ACL_TYPE_ACCESS ACL_TYPE_ACCESS
+#define SMB_ACL_TYPE_DEFAULT ACL_TYPE_DEFAULT
+
+#define SMB_ACL_VALID_NAME_BITS (4 | 2 | 1)
+#define SMB_ACL_VALID_OBJ_BITS (4 | 2 | 1)
+
+#define SMB_ACL_NEED_SORT
+
+#elif defined HAVE_TRU64_ACLS /*---------------------------------------------*/
+
+/* This is for DEC/Compaq Tru64 UNIX */
+
+#define SMB_ACL_TAG_T acl_tag_t
+#define SMB_ACL_TYPE_T acl_type_t
+
+/* Types of ACLs. */
+#define SMB_ACL_USER ACL_USER
+#define SMB_ACL_USER_OBJ ACL_USER_OBJ
+#define SMB_ACL_GROUP ACL_GROUP
+#define SMB_ACL_GROUP_OBJ ACL_GROUP_OBJ
+#define SMB_ACL_OTHER ACL_OTHER
+#define SMB_ACL_MASK ACL_MASK
+
+#define SMB_ACL_T acl_t
+
+#define SMB_ACL_ENTRY_T acl_entry_t
+
+#define SMB_ACL_FIRST_ENTRY 0
+#define SMB_ACL_NEXT_ENTRY 1
+
+#define SMB_ACL_TYPE_ACCESS ACL_TYPE_ACCESS
+#define SMB_ACL_TYPE_DEFAULT ACL_TYPE_DEFAULT
+
+#define SMB_ACL_VALID_NAME_BITS (4 | 2 | 1)
+#define SMB_ACL_VALID_OBJ_BITS (4 | 2 | 1)
+
+#define SMB_ACL_NEED_SORT
+
+#elif defined HAVE_UNIXWARE_ACLS || defined HAVE_SOLARIS_ACLS /*-------------*/
+
+/* Donated by Michael Davidson for UnixWare / OpenUNIX.
+ * Modified by Toomas Soome for Solaris. */
+
+/* SVR4.2 ES/MP ACLs */
+typedef int SMB_ACL_TAG_T;
+typedef int SMB_ACL_TYPE_T;
+
+/* Types of ACLs. */
+#define SMB_ACL_USER USER
+#define SMB_ACL_USER_OBJ USER_OBJ
+#define SMB_ACL_GROUP GROUP
+#define SMB_ACL_GROUP_OBJ GROUP_OBJ
+#define SMB_ACL_OTHER OTHER_OBJ
+#define SMB_ACL_MASK CLASS_OBJ
+
+typedef struct SMB_ACL_T {
+ int size;
+ int count;
+ int next;
+ struct acl acl[1];
+} *SMB_ACL_T;
+
+typedef struct acl *SMB_ACL_ENTRY_T;
+
+#define SMB_ACL_FIRST_ENTRY 0
+#define SMB_ACL_NEXT_ENTRY 1
+
+#define SMB_ACL_TYPE_ACCESS 0
+#define SMB_ACL_TYPE_DEFAULT 1
+
+#define SMB_ACL_VALID_NAME_BITS (4 | 2 | 1)
+#define SMB_ACL_VALID_OBJ_BITS (4 | 2 | 1)
+
+#define SMB_ACL_NEED_SORT
+
+#ifdef __CYGWIN__
+#define SMB_ACL_LOSES_SPECIAL_MODE_BITS
+#endif
+
+#elif defined HAVE_HPUX_ACLS /*----------------------------------------------*/
+
+/* Based on the Solaris & UnixWare code. */
+
+#undef GROUP
+#include
+
+/* SVR4.2 ES/MP ACLs */
+typedef int SMB_ACL_TAG_T;
+typedef int SMB_ACL_TYPE_T;
+
+/* Types of ACLs. */
+#define SMB_ACL_USER USER
+#define SMB_ACL_USER_OBJ USER_OBJ
+#define SMB_ACL_GROUP GROUP
+#define SMB_ACL_GROUP_OBJ GROUP_OBJ
+#define SMB_ACL_OTHER OTHER_OBJ
+#define SMB_ACL_MASK CLASS_OBJ
+
+typedef struct SMB_ACL_T {
+ int size;
+ int count;
+ int next;
+ struct acl acl[1];
+} *SMB_ACL_T;
+
+typedef struct acl *SMB_ACL_ENTRY_T;
+
+#define SMB_ACL_FIRST_ENTRY 0
+#define SMB_ACL_NEXT_ENTRY 1
+
+#define SMB_ACL_TYPE_ACCESS 0
+#define SMB_ACL_TYPE_DEFAULT 1
+
+#define SMB_ACL_VALID_NAME_BITS (4 | 2 | 1)
+#define SMB_ACL_VALID_OBJ_BITS (4 | 2 | 1)
+
+#define SMB_ACL_NEED_SORT
+
+#elif defined HAVE_IRIX_ACLS /*----------------------------------------------*/
+
+/* IRIX ACLs */
+
+#define SMB_ACL_TAG_T acl_tag_t
+#define SMB_ACL_TYPE_T acl_type_t
+
+/* Types of ACLs. */
+#define SMB_ACL_USER ACL_USER
+#define SMB_ACL_USER_OBJ ACL_USER_OBJ
+#define SMB_ACL_GROUP ACL_GROUP
+#define SMB_ACL_GROUP_OBJ ACL_GROUP_OBJ
+#define SMB_ACL_OTHER ACL_OTHER_OBJ
+#define SMB_ACL_MASK ACL_MASK
+
+typedef struct SMB_ACL_T {
+ int next;
+ BOOL freeaclp;
+ struct acl *aclp;
+} *SMB_ACL_T;
+
+#define SMB_ACL_ENTRY_T acl_entry_t
+
+#define SMB_ACL_FIRST_ENTRY 0
+#define SMB_ACL_NEXT_ENTRY 1
+
+#define SMB_ACL_TYPE_ACCESS ACL_TYPE_ACCESS
+#define SMB_ACL_TYPE_DEFAULT ACL_TYPE_DEFAULT
+
+#define SMB_ACL_VALID_NAME_BITS (4 | 2 | 1)
+#define SMB_ACL_VALID_OBJ_BITS (4 | 2 | 1)
+
+#define SMB_ACL_NEED_SORT
+
+#elif defined HAVE_AIX_ACLS /*-----------------------------------------------*/
+
+/* Donated by Medha Date, mdate@austin.ibm.com, for IBM */
+
+#include "/usr/include/acl.h"
+
+struct acl_entry_link{
+ struct acl_entry_link *prevp;
+ struct new_acl_entry *entryp;
+ struct acl_entry_link *nextp;
+ int count;
+};
+
+struct new_acl_entry{
+ unsigned short ace_len;
+ unsigned short ace_type;
+ unsigned int ace_access;
+ struct ace_id ace_id[1];
+};
+
+#define SMB_ACL_ENTRY_T struct new_acl_entry*
+#define SMB_ACL_T struct acl_entry_link*
+
+#define SMB_ACL_TAG_T unsigned short
+#define SMB_ACL_TYPE_T int
+
+/* Types of ACLs. */
+#define SMB_ACL_USER ACEID_USER
+#define SMB_ACL_USER_OBJ 3
+#define SMB_ACL_GROUP ACEID_GROUP
+#define SMB_ACL_GROUP_OBJ 4
+#define SMB_ACL_OTHER 5
+#define SMB_ACL_MASK 6
+
+#define SMB_ACL_FIRST_ENTRY 1
+#define SMB_ACL_NEXT_ENTRY 2
+
+#define SMB_ACL_TYPE_ACCESS 0
+#define SMB_ACL_TYPE_DEFAULT 1
+
+#define SMB_ACL_VALID_NAME_BITS (4 | 2 | 1)
+#define SMB_ACL_VALID_OBJ_BITS (4 | 2 | 1)
+
+#define SMB_ACL_NEED_SORT
+
+#elif defined(HAVE_OSX_ACLS) /*----------------------------------------------*/
+
+/* Special handling for OS X ACLs */
+
+#define SMB_ACL_TAG_T acl_tag_t
+#define SMB_ACL_TYPE_T acl_type_t
+
+#define SMB_ACL_T acl_t
+
+#define SMB_ACL_ENTRY_T acl_entry_t
+
+#define SMB_ACL_USER 1
+#define SMB_ACL_GROUP 2
+
+#define SMB_ACL_FIRST_ENTRY ACL_FIRST_ENTRY
+#define SMB_ACL_NEXT_ENTRY ACL_NEXT_ENTRY
+
+#define SMB_ACL_TYPE_ACCESS ACL_TYPE_EXTENDED
+#define SMB_ACL_TYPE_DEFAULT ACL_TYPE_DEFAULT
+
+#define SMB_ACL_VALID_NAME_BITS ((1<<25)-1)
+#define SMB_ACL_VALID_OBJ_BITS 0
+
+/*#undef SMB_ACL_NEED_SORT*/
+
+#else /*---------------------------------------------------------------------*/
+
+/* Unknown platform. */
+
+#error Cannot handle ACLs on this platform!
+
+#endif
+
+int sys_acl_get_entry(SMB_ACL_T the_acl, int entry_id, SMB_ACL_ENTRY_T *entry_p);
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p);
+int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *bits_p, id_t *u_g_id_p);
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type);
+SMB_ACL_T sys_acl_get_fd(int fd);
+SMB_ACL_T sys_acl_init(int count);
+int sys_acl_create_entry(SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry);
+int sys_acl_set_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype, uint32 bits, id_t u_g_id);
+int sys_acl_set_access_bits(SMB_ACL_ENTRY_T entry, uint32 bits);
+int sys_acl_valid(SMB_ACL_T theacl);
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl);
+int sys_acl_set_fd(int fd, SMB_ACL_T theacl);
+int sys_acl_delete_def_file(const char *name);
+int sys_acl_free_acl(SMB_ACL_T the_acl);
+int no_acl_syscall_error(int err);
+
+#endif /* SUPPORT_ACLS */
diff --git a/rsync/lib/sysxattrs.c b/rsync/lib/sysxattrs.c
new file mode 100644
index 0000000..f02802a
--- /dev/null
+++ b/rsync/lib/sysxattrs.c
@@ -0,0 +1,300 @@
+/*
+ * Extended attribute support for rsync.
+ *
+ * Copyright (C) 2004 Red Hat, Inc.
+ * Copyright (C) 2003-2014 Wayne Davison
+ * Written by Jay Fenlason.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "sysxattrs.h"
+
+#ifdef SUPPORT_XATTRS
+
+#ifdef HAVE_OSX_XATTRS
+#define GETXATTR_FETCH_LIMIT (64*1024*1024)
+#endif
+
+#if defined HAVE_LINUX_XATTRS
+
+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
+{
+ return lgetxattr(path, name, value, size);
+}
+
+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
+{
+ return fgetxattr(filedes, name, value, size);
+}
+
+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
+{
+ return lsetxattr(path, name, value, size, 0);
+}
+
+int sys_lremovexattr(const char *path, const char *name)
+{
+ return lremovexattr(path, name);
+}
+
+ssize_t sys_llistxattr(const char *path, char *list, size_t size)
+{
+ return llistxattr(path, list, size);
+}
+
+#elif HAVE_OSX_XATTRS
+
+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
+{
+ ssize_t len = getxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
+
+ /* If we're retrieving data, handle resource forks > 64MB specially */
+ if (value != NULL && len == GETXATTR_FETCH_LIMIT && (size_t)len < size) {
+ /* getxattr will only return 64MB of data at a time, need to call again with a new offset */
+ u_int32_t offset = len;
+ size_t data_retrieved = len;
+ while (data_retrieved < size) {
+ len = getxattr(path, name, value + offset, size - data_retrieved, offset, XATTR_NOFOLLOW);
+ if (len <= 0)
+ break;
+ data_retrieved += len;
+ offset += (u_int32_t)len;
+ }
+ len = data_retrieved;
+ }
+
+ return len;
+}
+
+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
+{
+ return fgetxattr(filedes, name, value, size, 0, 0);
+}
+
+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
+{
+ return setxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
+}
+
+int sys_lremovexattr(const char *path, const char *name)
+{
+ return removexattr(path, name, XATTR_NOFOLLOW);
+}
+
+ssize_t sys_llistxattr(const char *path, char *list, size_t size)
+{
+ return listxattr(path, list, size, XATTR_NOFOLLOW);
+}
+
+#elif HAVE_FREEBSD_XATTRS
+
+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
+{
+ return extattr_get_link(path, EXTATTR_NAMESPACE_USER, name, value, size);
+}
+
+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
+{
+ return extattr_get_fd(filedes, EXTATTR_NAMESPACE_USER, name, value, size);
+}
+
+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
+{
+ return extattr_set_link(path, EXTATTR_NAMESPACE_USER, name, value, size);
+}
+
+int sys_lremovexattr(const char *path, const char *name)
+{
+ return extattr_delete_link(path, EXTATTR_NAMESPACE_USER, name);
+}
+
+ssize_t sys_llistxattr(const char *path, char *list, size_t size)
+{
+ unsigned char keylen;
+ ssize_t off, len = extattr_list_link(path, EXTATTR_NAMESPACE_USER, list, size);
+
+ if (len <= 0 || (size_t)len > size)
+ return len;
+
+ /* FreeBSD puts a single-byte length before each string, with no '\0'
+ * terminator. We need to change this into a series of null-terminted
+ * strings. Since the size is the same, we can simply transform the
+ * output in place. */
+ for (off = 0; off < len; off += keylen + 1) {
+ keylen = ((unsigned char*)list)[off];
+ if (off + keylen >= len) {
+ /* Should be impossible, but kernel bugs happen! */
+ errno = EINVAL;
+ return -1;
+ }
+ memmove(list+off, list+off+1, keylen);
+ list[off+keylen] = '\0';
+ }
+
+ return len;
+}
+
+#elif HAVE_SOLARIS_XATTRS
+
+static ssize_t read_xattr(int attrfd, void *buf, size_t buflen)
+{
+ STRUCT_STAT sb;
+ ssize_t ret;
+
+ if (fstat(attrfd, &sb) < 0)
+ ret = -1;
+ else if (sb.st_size > SSIZE_MAX) {
+ errno = ERANGE;
+ ret = -1;
+ } else if (buflen == 0)
+ ret = sb.st_size;
+ else if (sb.st_size > buflen) {
+ errno = ERANGE;
+ ret = -1;
+ } else {
+ size_t bufpos;
+ for (bufpos = 0; bufpos < sb.st_size; ) {
+ ssize_t cnt = read(attrfd, buf + bufpos, sb.st_size - bufpos);
+ if (cnt <= 0) {
+ if (cnt < 0 && errno == EINTR)
+ continue;
+ bufpos = -1;
+ break;
+ }
+ bufpos += cnt;
+ }
+ ret = bufpos;
+ }
+
+ close(attrfd);
+
+ return ret;
+}
+
+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
+{
+ int attrfd;
+
+ if ((attrfd = attropen(path, name, O_RDONLY)) < 0) {
+ errno = ENOATTR;
+ return -1;
+ }
+
+ return read_xattr(attrfd, value, size);
+}
+
+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
+{
+ int attrfd;
+
+ if ((attrfd = openat(filedes, name, O_RDONLY|O_XATTR, 0)) < 0) {
+ errno = ENOATTR;
+ return -1;
+ }
+
+ return read_xattr(attrfd, value, size);
+}
+
+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
+{
+ int attrfd;
+ size_t bufpos;
+ mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
+
+ if ((attrfd = attropen(path, name, O_CREAT|O_TRUNC|O_WRONLY, mode)) < 0)
+ return -1;
+
+ for (bufpos = 0; bufpos < size; ) {
+ ssize_t cnt = write(attrfd, value+bufpos, size);
+ if (cnt <= 0) {
+ if (cnt < 0 && errno == EINTR)
+ continue;
+ bufpos = -1;
+ break;
+ }
+ bufpos += cnt;
+ }
+
+ close(attrfd);
+
+ return bufpos > 0 ? 0 : -1;
+}
+
+int sys_lremovexattr(const char *path, const char *name)
+{
+ int attrdirfd;
+ int ret;
+
+ if ((attrdirfd = attropen(path, ".", O_RDONLY)) < 0)
+ return -1;
+
+ ret = unlinkat(attrdirfd, name, 0);
+
+ close(attrdirfd);
+
+ return ret;
+}
+
+ssize_t sys_llistxattr(const char *path, char *list, size_t size)
+{
+ int attrdirfd;
+ DIR *dirp;
+ struct dirent *dp;
+ ssize_t ret = 0;
+
+ if ((attrdirfd = attropen(path, ".", O_RDONLY)) < 0) {
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ if ((dirp = fdopendir(attrdirfd)) == NULL) {
+ close(attrdirfd);
+ return -1;
+ }
+
+ while ((dp = readdir(dirp))) {
+ int len = strlen(dp->d_name);
+
+ if (dp->d_name[0] == '.' && (len == 1 || (len == 2 && dp->d_name[1] == '.')))
+ continue;
+ if (len == 11 && dp->d_name[0] == 'S' && strncmp(dp->d_name, "SUNWattr_r", 10) == 0
+ && (dp->d_name[10] == 'o' || dp->d_name[10] == 'w'))
+ continue;
+
+ if ((ret += len+1) > size) {
+ if (size == 0)
+ continue;
+ ret = -1;
+ errno = ERANGE;
+ break;
+ }
+ memcpy(list, dp->d_name, len+1);
+ list += len+1;
+ }
+
+ closedir(dirp);
+ close(attrdirfd);
+
+ return ret;
+}
+
+#else
+
+#error You need to create xattr compatibility functions.
+
+#endif
+
+#endif /* SUPPORT_XATTRS */
diff --git a/rsync/lib/sysxattrs.h b/rsync/lib/sysxattrs.h
new file mode 100644
index 0000000..428421a
--- /dev/null
+++ b/rsync/lib/sysxattrs.h
@@ -0,0 +1,26 @@
+#ifdef SUPPORT_XATTRS
+
+#if defined HAVE_ATTR_XATTR_H
+#include
+#elif defined HAVE_SYS_XATTR_H
+#include
+#elif defined HAVE_SYS_EXTATTR_H
+#include
+#endif
+
+/* Linux 2.4 does not define this as a distinct errno value: */
+#ifndef ENOATTR
+#define ENOATTR ENODATA
+#endif
+
+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size);
+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size);
+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size);
+int sys_lremovexattr(const char *path, const char *name);
+ssize_t sys_llistxattr(const char *path, char *list, size_t size);
+
+#else
+
+/* No xattrs available */
+
+#endif
diff --git a/rsync/lib/wildmatch.c b/rsync/lib/wildmatch.c
new file mode 100644
index 0000000..f3a1731
--- /dev/null
+++ b/rsync/lib/wildmatch.c
@@ -0,0 +1,368 @@
+/*
+** Do shell-style pattern matching for ?, \, [], and * characters.
+** It is 8bit clean.
+**
+** Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
+** Rich $alz is now .
+**
+** Modified by Wayne Davison to special-case '/' matching, to make '**'
+** work differently than '*', and to fix the character-class code.
+*/
+
+#include "rsync.h"
+
+/* What character marks an inverted character class? */
+#define NEGATE_CLASS '!'
+#define NEGATE_CLASS2 '^'
+
+#define FALSE 0
+#define TRUE 1
+#define ABORT_ALL -1
+#define ABORT_TO_STARSTAR -2
+
+#define CC_EQ(class, len, litmatch) ((len) == sizeof (litmatch)-1 \
+ && *(class) == *(litmatch) \
+ && strncmp((char*)class, litmatch, len) == 0)
+
+#if defined STDC_HEADERS || !defined isascii
+# define ISASCII(c) 1
+#else
+# define ISASCII(c) isascii(c)
+#endif
+
+#ifdef isblank
+# define ISBLANK(c) (ISASCII(c) && isblank(c))
+#else
+# define ISBLANK(c) ((c) == ' ' || (c) == '\t')
+#endif
+
+#ifdef isgraph
+# define ISGRAPH(c) (ISASCII(c) && isgraph(c))
+#else
+# define ISGRAPH(c) (ISASCII(c) && isprint(c) && !isspace(c))
+#endif
+
+#define ISPRINT(c) (ISASCII(c) && isprint(c))
+#define ISDIGIT(c) (ISASCII(c) && isdigit(c))
+#define ISALNUM(c) (ISASCII(c) && isalnum(c))
+#define ISALPHA(c) (ISASCII(c) && isalpha(c))
+#define ISCNTRL(c) (ISASCII(c) && iscntrl(c))
+#define ISLOWER(c) (ISASCII(c) && islower(c))
+#define ISPUNCT(c) (ISASCII(c) && ispunct(c))
+#define ISSPACE(c) (ISASCII(c) && isspace(c))
+#define ISUPPER(c) (ISASCII(c) && isupper(c))
+#define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))
+
+#ifdef WILD_TEST_ITERATIONS
+int wildmatch_iteration_count;
+#endif
+
+static int force_lower_case = 0;
+
+/* Match pattern "p" against the a virtually-joined string consisting
+ * of "text" and any strings in array "a". */
+static int dowild(const uchar *p, const uchar *text, const uchar*const *a)
+{
+ uchar p_ch;
+
+#ifdef WILD_TEST_ITERATIONS
+ wildmatch_iteration_count++;
+#endif
+
+ for ( ; (p_ch = *p) != '\0'; text++, p++) {
+ int matched, special;
+ uchar t_ch, prev_ch;
+ while ((t_ch = *text) == '\0') {
+ if (*a == NULL) {
+ if (p_ch != '*')
+ return ABORT_ALL;
+ break;
+ }
+ text = *a++;
+ }
+ if (force_lower_case && ISUPPER(t_ch))
+ t_ch = tolower(t_ch);
+ switch (p_ch) {
+ case '\\':
+ /* Literal match with following character. Note that the test
+ * in "default" handles the p[1] == '\0' failure case. */
+ p_ch = *++p;
+ /* FALLTHROUGH */
+ default:
+ if (t_ch != p_ch)
+ return FALSE;
+ continue;
+ case '?':
+ /* Match anything but '/'. */
+ if (t_ch == '/')
+ return FALSE;
+ continue;
+ case '*':
+ if (*++p == '*') {
+ while (*++p == '*') {}
+ special = TRUE;
+ } else
+ special = FALSE;
+ if (*p == '\0') {
+ /* Trailing "**" matches everything. Trailing "*" matches
+ * only if there are no more slash characters. */
+ if (!special) {
+ do {
+ if (strchr((char*)text, '/') != NULL)
+ return FALSE;
+ } while ((text = *a++) != NULL);
+ }
+ return TRUE;
+ }
+ while (1) {
+ if (t_ch == '\0') {
+ if ((text = *a++) == NULL)
+ break;
+ t_ch = *text;
+ continue;
+ }
+ if ((matched = dowild(p, text, a)) != FALSE) {
+ if (!special || matched != ABORT_TO_STARSTAR)
+ return matched;
+ } else if (!special && t_ch == '/')
+ return ABORT_TO_STARSTAR;
+ t_ch = *++text;
+ }
+ return ABORT_ALL;
+ case '[':
+ p_ch = *++p;
+#ifdef NEGATE_CLASS2
+ if (p_ch == NEGATE_CLASS2)
+ p_ch = NEGATE_CLASS;
+#endif
+ /* Assign literal TRUE/FALSE because of "matched" comparison. */
+ special = p_ch == NEGATE_CLASS? TRUE : FALSE;
+ if (special) {
+ /* Inverted character class. */
+ p_ch = *++p;
+ }
+ prev_ch = 0;
+ matched = FALSE;
+ do {
+ if (!p_ch)
+ return ABORT_ALL;
+ if (p_ch == '\\') {
+ p_ch = *++p;
+ if (!p_ch)
+ return ABORT_ALL;
+ if (t_ch == p_ch)
+ matched = TRUE;
+ } else if (p_ch == '-' && prev_ch && p[1] && p[1] != ']') {
+ p_ch = *++p;
+ if (p_ch == '\\') {
+ p_ch = *++p;
+ if (!p_ch)
+ return ABORT_ALL;
+ }
+ if (t_ch <= p_ch && t_ch >= prev_ch)
+ matched = TRUE;
+ p_ch = 0; /* This makes "prev_ch" get set to 0. */
+ } else if (p_ch == '[' && p[1] == ':') {
+ const uchar *s;
+ int i;
+ for (s = p += 2; (p_ch = *p) && p_ch != ']'; p++) {} /*SHARED ITERATOR*/
+ if (!p_ch)
+ return ABORT_ALL;
+ i = p - s - 1;
+ if (i < 0 || p[-1] != ':') {
+ /* Didn't find ":]", so treat like a normal set. */
+ p = s - 2;
+ p_ch = '[';
+ if (t_ch == p_ch)
+ matched = TRUE;
+ continue;
+ }
+ if (CC_EQ(s,i, "alnum")) {
+ if (ISALNUM(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "alpha")) {
+ if (ISALPHA(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "blank")) {
+ if (ISBLANK(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "cntrl")) {
+ if (ISCNTRL(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "digit")) {
+ if (ISDIGIT(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "graph")) {
+ if (ISGRAPH(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "lower")) {
+ if (ISLOWER(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "print")) {
+ if (ISPRINT(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "punct")) {
+ if (ISPUNCT(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "space")) {
+ if (ISSPACE(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "upper")) {
+ if (ISUPPER(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "xdigit")) {
+ if (ISXDIGIT(t_ch))
+ matched = TRUE;
+ } else /* malformed [:class:] string */
+ return ABORT_ALL;
+ p_ch = 0; /* This makes "prev_ch" get set to 0. */
+ } else if (t_ch == p_ch)
+ matched = TRUE;
+ } while (prev_ch = p_ch, (p_ch = *++p) != ']');
+ if (matched == special || t_ch == '/')
+ return FALSE;
+ continue;
+ }
+ }
+
+ do {
+ if (*text)
+ return FALSE;
+ } while ((text = *a++) != NULL);
+
+ return TRUE;
+}
+
+/* Match literal string "s" against the a virtually-joined string consisting
+ * of "text" and any strings in array "a". */
+static int doliteral(const uchar *s, const uchar *text, const uchar*const *a)
+{
+ for ( ; *s != '\0'; text++, s++) {
+ while (*text == '\0') {
+ if ((text = *a++) == NULL)
+ return FALSE;
+ }
+ if (*text != *s)
+ return FALSE;
+ }
+
+ do {
+ if (*text)
+ return FALSE;
+ } while ((text = *a++) != NULL);
+
+ return TRUE;
+}
+
+/* Return the last "count" path elements from the concatenated string.
+ * We return a string pointer to the start of the string, and update the
+ * array pointer-pointer to point to any remaining string elements. */
+static const uchar *trailing_N_elements(const uchar*const **a_ptr, int count)
+{
+ const uchar*const *a = *a_ptr;
+ const uchar*const *first_a = a;
+
+ while (*a)
+ a++;
+
+ while (a != first_a) {
+ const uchar *s = *--a;
+ s += strlen((char*)s);
+ while (--s >= *a) {
+ if (*s == '/' && !--count) {
+ *a_ptr = a+1;
+ return s+1;
+ }
+ }
+ }
+
+ if (count == 1) {
+ *a_ptr = a+1;
+ return *a;
+ }
+
+ return NULL;
+}
+
+/* Match the "pattern" against the "text" string. */
+int wildmatch(const char *pattern, const char *text)
+{
+ static const uchar *nomore[1]; /* A NULL pointer. */
+#ifdef WILD_TEST_ITERATIONS
+ wildmatch_iteration_count = 0;
+#endif
+ return dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;
+}
+
+/* Match the "pattern" against the forced-to-lower-case "text" string. */
+int iwildmatch(const char *pattern, const char *text)
+{
+ static const uchar *nomore[1]; /* A NULL pointer. */
+ int ret;
+#ifdef WILD_TEST_ITERATIONS
+ wildmatch_iteration_count = 0;
+#endif
+ force_lower_case = 1;
+ ret = dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;
+ force_lower_case = 0;
+ return ret;
+}
+
+/* Match pattern "p" against the a virtually-joined string consisting
+ * of all the pointers in array "texts" (which has a NULL pointer at the
+ * end). The int "where" can be 0 (normal matching), > 0 (match only
+ * the trailing N slash-separated filename components of "texts"), or < 0
+ * (match the "pattern" at the start or after any slash in "texts"). */
+int wildmatch_array(const char *pattern, const char*const *texts, int where)
+{
+ const uchar *p = (const uchar*)pattern;
+ const uchar*const *a = (const uchar*const*)texts;
+ const uchar *text;
+ int matched;
+
+#ifdef WILD_TEST_ITERATIONS
+ wildmatch_iteration_count = 0;
+#endif
+
+ if (where > 0)
+ text = trailing_N_elements(&a, where);
+ else
+ text = *a++;
+ if (!text)
+ return FALSE;
+
+ if ((matched = dowild(p, text, a)) != TRUE && where < 0
+ && matched != ABORT_ALL) {
+ while (1) {
+ if (*text == '\0') {
+ if ((text = (uchar*)*a++) == NULL)
+ return FALSE;
+ continue;
+ }
+ if (*text++ == '/' && (matched = dowild(p, text, a)) != FALSE
+ && matched != ABORT_TO_STARSTAR)
+ break;
+ }
+ }
+ return matched == TRUE;
+}
+
+/* Match literal string "s" against the a virtually-joined string consisting
+ * of all the pointers in array "texts" (which has a NULL pointer at the
+ * end). The int "where" can be 0 (normal matching), or > 0 (match
+ * only the trailing N slash-separated filename components of "texts"). */
+int litmatch_array(const char *string, const char*const *texts, int where)
+{
+ const uchar *s = (const uchar*)string;
+ const uchar*const *a = (const uchar* const*)texts;
+ const uchar *text;
+
+ if (where > 0)
+ text = trailing_N_elements(&a, where);
+ else
+ text = *a++;
+ if (!text)
+ return FALSE;
+
+ return doliteral(s, text, a) == TRUE;
+}
diff --git a/rsync/lib/wildmatch.h b/rsync/lib/wildmatch.h
new file mode 100644
index 0000000..e7f1a35
--- /dev/null
+++ b/rsync/lib/wildmatch.h
@@ -0,0 +1,6 @@
+/* wildmatch.h */
+
+int wildmatch(const char *pattern, const char *text);
+int iwildmatch(const char *pattern, const char *text);
+int wildmatch_array(const char *pattern, const char*const *texts, int where);
+int litmatch_array(const char *string, const char*const *texts, int where);
diff --git a/rsync/loadparm.c b/rsync/loadparm.c
new file mode 100644
index 0000000..a37bbae
--- /dev/null
+++ b/rsync/loadparm.c
@@ -0,0 +1,846 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+/* This is based on loadparm.c from Samba, written by Andrew Tridgell
+ * and Karl Auer. Some of the changes are:
+ *
+ * Copyright (C) 2001, 2002 Martin Pool
+ * Copyright (C) 2003-2014 Wayne Davison
+ */
+
+/* Load parameters.
+ *
+ * This module provides suitable callback functions for the params
+ * module. It builds the internal table of section details which is
+ * then used by the rest of the server.
+ *
+ * To add a parameter:
+ *
+ * 1) add it to the global_vars or local_vars structure definition
+ * 2) add it to the parm_table
+ * 3) add it to the list of available functions (eg: using FN_GLOBAL_STRING())
+ * 4) initialise it in the Defaults static stucture
+ *
+ * Notes:
+ * The configuration file is processed sequentially for speed. For this
+ * reason, there is a fair bit of sequence-dependent code here - ie., code
+ * which assumes that certain things happen before others. In particular, the
+ * code which happens at the boundary between sections is delicately poised,
+ * so be careful!
+ */
+
+#include "rsync.h"
+#include "itypes.h"
+
+extern item_list dparam_list;
+
+#define strequal(a, b) (strcasecmp(a, b)==0)
+#define BOOLSTR(b) ((b) ? "Yes" : "No")
+
+#ifndef LOG_DAEMON
+#define LOG_DAEMON 0
+#endif
+
+#define DEFAULT_DONT_COMPRESS "*.gz *.zip *.z *.rpm *.deb *.iso *.bz2" \
+ " *.t[gb]z *.7z *.mp[34] *.mov *.avi *.ogg *.jpg *.jpeg *.png" \
+ " *.lzo *.rzip *.lzma *.rar *.ace *.gpg *.xz *.txz *.lz *.tlz"
+
+/* the following are used by loadparm for option lists */
+typedef enum {
+ P_BOOL, P_BOOLREV, P_CHAR, P_INTEGER,
+ P_OCTAL, P_PATH, P_STRING, P_ENUM
+} parm_type;
+
+typedef enum {
+ P_LOCAL, P_GLOBAL, P_NONE
+} parm_class;
+
+struct enum_list {
+ int value;
+ char *name;
+};
+
+struct parm_struct {
+ char *label;
+ parm_type type;
+ parm_class class;
+ void *ptr;
+ struct enum_list *enum_list;
+ unsigned flags;
+};
+
+#ifndef GLOBAL_NAME
+#define GLOBAL_NAME "global"
+#endif
+
+/* some helpful bits */
+#define iSECTION(i) ((local_vars*)section_list.items)[i]
+#define LP_SNUM_OK(i) ((i) >= 0 && (i) < (int)section_list.count)
+#define SECTION_PTR(s, p) (((char*)(s)) + (ptrdiff_t)(((char*)(p))-(char*)&Vars.l))
+
+/* This structure describes global (ie., server-wide) parameters. */
+typedef struct {
+ char *bind_address;
+ char *motd_file;
+ char *pid_file;
+ char *socket_options;
+
+ int listen_backlog;
+ int rsync_port;
+} global_vars;
+
+/* This structure describes a single section. Their order must match the
+ * initializers below, which you can accomplish by keeping each sub-section
+ * sorted. (e.g. in vim, just visually select each subsection and use !sort.)
+ * NOTE: the char* variables MUST all remain at the start of the stuct! */
+typedef struct {
+ char *auth_users;
+ char *charset;
+ char *comment;
+ char *dont_compress;
+ char *exclude;
+ char *exclude_from;
+ char *filter;
+ char *gid;
+ char *hosts_allow;
+ char *hosts_deny;
+ char *include;
+ char *include_from;
+ char *incoming_chmod;
+ char *lock_file;
+ char *log_file;
+ char *log_format;
+ char *name;
+ char *outgoing_chmod;
+ char *path;
+ char *postxfer_exec;
+ char *prexfer_exec;
+ char *refuse_options;
+ char *secrets_file;
+ char *temp_dir;
+ char *uid;
+/* NOTE: update this macro if the last char* variable changes! */
+#define LOCAL_STRING_COUNT() (offsetof(local_vars, uid) / sizeof (char*) + 1)
+
+ int max_connections;
+ int max_verbosity;
+ int syslog_facility;
+ int timeout;
+
+ BOOL fake_super;
+ BOOL forward_lookup;
+ BOOL ignore_errors;
+ BOOL ignore_nonreadable;
+ BOOL list;
+ BOOL munge_symlinks;
+ BOOL numeric_ids;
+ BOOL read_only;
+ BOOL reverse_lookup;
+ BOOL strict_modes;
+ BOOL transfer_logging;
+ BOOL use_chroot;
+ BOOL write_only;
+} local_vars;
+
+/* This structure describes the global variables (g) as well as the globally
+ * specified values of the local variables (l), which are used when modules
+ * don't specify their own values. */
+typedef struct {
+ global_vars g;
+ local_vars l;
+} all_vars;
+
+/* The application defaults for all the variables. "Defaults" is
+ * used to re-initialize "Vars" before each config-file read.
+ *
+ * In order to keep these sorted in the same way as the structure
+ * above, use the variable name in the leading comment, including a
+ * trailing ';' (to avoid a sorting problem with trailing digits). */
+static const all_vars Defaults = {
+ /* ==== global_vars ==== */
+ {
+ /* bind_address; */ NULL,
+ /* motd_file; */ NULL,
+ /* pid_file; */ NULL,
+ /* socket_options; */ NULL,
+
+ /* listen_backlog; */ 5,
+ /* rsync_port; */ 0,
+ },
+
+ /* ==== local_vars ==== */
+ {
+ /* auth_users; */ NULL,
+ /* charset; */ NULL,
+ /* comment; */ NULL,
+ /* dont_compress; */ DEFAULT_DONT_COMPRESS,
+ /* exclude; */ NULL,
+ /* exclude_from; */ NULL,
+ /* filter; */ NULL,
+ /* gid; */ NULL,
+ /* hosts_allow; */ NULL,
+ /* hosts_deny; */ NULL,
+ /* include; */ NULL,
+ /* include_from; */ NULL,
+ /* incoming_chmod; */ NULL,
+ /* lock_file; */ DEFAULT_LOCK_FILE,
+ /* log_file; */ NULL,
+ /* log_format; */ "%o %h [%a] %m (%u) %f %l",
+ /* name; */ NULL,
+ /* outgoing_chmod; */ NULL,
+ /* path; */ NULL,
+ /* postxfer_exec; */ NULL,
+ /* prexfer_exec; */ NULL,
+ /* refuse_options; */ NULL,
+ /* secrets_file; */ NULL,
+ /* temp_dir; */ NULL,
+ /* uid; */ NULL,
+
+ /* max_connections; */ 0,
+ /* max_verbosity; */ 1,
+ /* syslog_facility; */ LOG_DAEMON,
+ /* timeout; */ 0,
+
+ /* fake_super; */ False,
+ /* forward_lookup; */ True,
+ /* ignore_errors; */ False,
+ /* ignore_nonreadable; */ False,
+ /* list; */ True,
+ /* munge_symlinks; */ (BOOL)-1,
+ /* numeric_ids; */ (BOOL)-1,
+ /* read_only; */ True,
+ /* reverse_lookup; */ True,
+ /* strict_modes; */ True,
+ /* transfer_logging; */ False,
+ /* use_chroot; */ True,
+ /* write_only; */ False,
+ }
+};
+
+/* The currently configured values for all the variables. */
+static all_vars Vars;
+
+/* Stack of "Vars" values used by the &include directive. */
+static item_list Vars_stack = EMPTY_ITEM_LIST;
+
+/* The array of section values that holds all the defined modules. */
+static item_list section_list = EMPTY_ITEM_LIST;
+
+static int iSectionIndex = -1;
+static BOOL bInGlobalSection = True;
+
+#define NUMPARAMETERS (sizeof (parm_table) / sizeof (struct parm_struct))
+
+static struct enum_list enum_facilities[] = {
+#ifdef LOG_AUTH
+ { LOG_AUTH, "auth" },
+#endif
+#ifdef LOG_AUTHPRIV
+ { LOG_AUTHPRIV, "authpriv" },
+#endif
+#ifdef LOG_CRON
+ { LOG_CRON, "cron" },
+#endif
+#ifdef LOG_DAEMON
+ { LOG_DAEMON, "daemon" },
+#endif
+#ifdef LOG_FTP
+ { LOG_FTP, "ftp" },
+#endif
+#ifdef LOG_KERN
+ { LOG_KERN, "kern" },
+#endif
+#ifdef LOG_LPR
+ { LOG_LPR, "lpr" },
+#endif
+#ifdef LOG_MAIL
+ { LOG_MAIL, "mail" },
+#endif
+#ifdef LOG_NEWS
+ { LOG_NEWS, "news" },
+#endif
+#ifdef LOG_AUTH
+ { LOG_AUTH, "security" },
+#endif
+#ifdef LOG_SYSLOG
+ { LOG_SYSLOG, "syslog" },
+#endif
+#ifdef LOG_USER
+ { LOG_USER, "user" },
+#endif
+#ifdef LOG_UUCP
+ { LOG_UUCP, "uucp" },
+#endif
+#ifdef LOG_LOCAL0
+ { LOG_LOCAL0, "local0" },
+#endif
+#ifdef LOG_LOCAL1
+ { LOG_LOCAL1, "local1" },
+#endif
+#ifdef LOG_LOCAL2
+ { LOG_LOCAL2, "local2" },
+#endif
+#ifdef LOG_LOCAL3
+ { LOG_LOCAL3, "local3" },
+#endif
+#ifdef LOG_LOCAL4
+ { LOG_LOCAL4, "local4" },
+#endif
+#ifdef LOG_LOCAL5
+ { LOG_LOCAL5, "local5" },
+#endif
+#ifdef LOG_LOCAL6
+ { LOG_LOCAL6, "local6" },
+#endif
+#ifdef LOG_LOCAL7
+ { LOG_LOCAL7, "local7" },
+#endif
+ { -1, NULL }
+};
+
+static struct parm_struct parm_table[] =
+{
+ {"address", P_STRING, P_GLOBAL,&Vars.g.bind_address, NULL,0},
+ {"listen backlog", P_INTEGER,P_GLOBAL,&Vars.g.listen_backlog, NULL,0},
+ {"motd file", P_STRING, P_GLOBAL,&Vars.g.motd_file, NULL,0},
+ {"pid file", P_STRING, P_GLOBAL,&Vars.g.pid_file, NULL,0},
+ {"port", P_INTEGER,P_GLOBAL,&Vars.g.rsync_port, NULL,0},
+ {"socket options", P_STRING, P_GLOBAL,&Vars.g.socket_options, NULL,0},
+
+ {"auth users", P_STRING, P_LOCAL, &Vars.l.auth_users, NULL,0},
+ {"charset", P_STRING, P_LOCAL, &Vars.l.charset, NULL,0},
+ {"comment", P_STRING, P_LOCAL, &Vars.l.comment, NULL,0},
+ {"dont compress", P_STRING, P_LOCAL, &Vars.l.dont_compress, NULL,0},
+ {"exclude from", P_STRING, P_LOCAL, &Vars.l.exclude_from, NULL,0},
+ {"exclude", P_STRING, P_LOCAL, &Vars.l.exclude, NULL,0},
+ {"fake super", P_BOOL, P_LOCAL, &Vars.l.fake_super, NULL,0},
+ {"filter", P_STRING, P_LOCAL, &Vars.l.filter, NULL,0},
+ {"forward lookup", P_BOOL, P_LOCAL, &Vars.l.forward_lookup, NULL,0},
+ {"gid", P_STRING, P_LOCAL, &Vars.l.gid, NULL,0},
+ {"hosts allow", P_STRING, P_LOCAL, &Vars.l.hosts_allow, NULL,0},
+ {"hosts deny", P_STRING, P_LOCAL, &Vars.l.hosts_deny, NULL,0},
+ {"ignore errors", P_BOOL, P_LOCAL, &Vars.l.ignore_errors, NULL,0},
+ {"ignore nonreadable",P_BOOL, P_LOCAL, &Vars.l.ignore_nonreadable, NULL,0},
+ {"include from", P_STRING, P_LOCAL, &Vars.l.include_from, NULL,0},
+ {"include", P_STRING, P_LOCAL, &Vars.l.include, NULL,0},
+ {"incoming chmod", P_STRING, P_LOCAL, &Vars.l.incoming_chmod, NULL,0},
+ {"list", P_BOOL, P_LOCAL, &Vars.l.list, NULL,0},
+ {"lock file", P_STRING, P_LOCAL, &Vars.l.lock_file, NULL,0},
+ {"log file", P_STRING, P_LOCAL, &Vars.l.log_file, NULL,0},
+ {"log format", P_STRING, P_LOCAL, &Vars.l.log_format, NULL,0},
+ {"max connections", P_INTEGER,P_LOCAL, &Vars.l.max_connections, NULL,0},
+ {"max verbosity", P_INTEGER,P_LOCAL, &Vars.l.max_verbosity, NULL,0},
+ {"munge symlinks", P_BOOL, P_LOCAL, &Vars.l.munge_symlinks, NULL,0},
+ {"name", P_STRING, P_LOCAL, &Vars.l.name, NULL,0},
+ {"numeric ids", P_BOOL, P_LOCAL, &Vars.l.numeric_ids, NULL,0},
+ {"outgoing chmod", P_STRING, P_LOCAL, &Vars.l.outgoing_chmod, NULL,0},
+ {"path", P_PATH, P_LOCAL, &Vars.l.path, NULL,0},
+#ifdef HAVE_PUTENV
+ {"post-xfer exec", P_STRING, P_LOCAL, &Vars.l.postxfer_exec, NULL,0},
+ {"pre-xfer exec", P_STRING, P_LOCAL, &Vars.l.prexfer_exec, NULL,0},
+#endif
+ {"read only", P_BOOL, P_LOCAL, &Vars.l.read_only, NULL,0},
+ {"refuse options", P_STRING, P_LOCAL, &Vars.l.refuse_options, NULL,0},
+ {"reverse lookup", P_BOOL, P_LOCAL, &Vars.l.reverse_lookup, NULL,0},
+ {"secrets file", P_STRING, P_LOCAL, &Vars.l.secrets_file, NULL,0},
+ {"strict modes", P_BOOL, P_LOCAL, &Vars.l.strict_modes, NULL,0},
+ {"syslog facility", P_ENUM, P_LOCAL, &Vars.l.syslog_facility, enum_facilities,0},
+ {"temp dir", P_PATH, P_LOCAL, &Vars.l.temp_dir, NULL,0},
+ {"timeout", P_INTEGER,P_LOCAL, &Vars.l.timeout, NULL,0},
+ {"transfer logging", P_BOOL, P_LOCAL, &Vars.l.transfer_logging, NULL,0},
+ {"uid", P_STRING, P_LOCAL, &Vars.l.uid, NULL,0},
+ {"use chroot", P_BOOL, P_LOCAL, &Vars.l.use_chroot, NULL,0},
+ {"write only", P_BOOL, P_LOCAL, &Vars.l.write_only, NULL,0},
+ {NULL, P_BOOL, P_NONE, NULL, NULL,0}
+};
+
+/* Initialise the Default all_vars structure. */
+static void reset_all_vars(void)
+{
+ memcpy(&Vars, &Defaults, sizeof Vars);
+}
+
+/* Expand %VAR% references. Any unknown vars or unrecognized
+ * syntax leaves the raw chars unchanged. */
+static char *expand_vars(char *str)
+{
+ char *buf, *t, *f;
+ int bufsize;
+
+ if (strchr(str, '%') == NULL)
+ return str;
+
+ bufsize = strlen(str) + 2048;
+ if ((buf = new_array(char, bufsize+1)) == NULL) /* +1 for trailing '\0' */
+ out_of_memory("expand_vars");
+
+ for (t = buf, f = str; bufsize && *f; ) {
+ if (*f == '%' && *++f != '%') {
+ char *percent = strchr(f, '%');
+ if (percent) {
+ char *val;
+ *percent = '\0';
+ val = getenv(f);
+ *percent = '%';
+ if (val) {
+ int len = strlcpy(t, val, bufsize+1);
+ if (len > bufsize)
+ break;
+ bufsize -= len;
+ t += len;
+ f = percent + 1;
+ continue;
+ }
+ }
+ f--;
+ }
+ *t++ = *f++;
+ bufsize--;
+ }
+ *t = '\0';
+
+ if (*f) {
+ rprintf(FLOG, "Overflowed buf in expand_vars() trying to expand: %s\n", str);
+ exit_cleanup(RERR_MALLOC);
+ }
+
+ if (bufsize && (buf = realloc(buf, t - buf + 1)) == NULL)
+ out_of_memory("expand_vars");
+
+ return buf;
+}
+
+/* In this section all the functions that are used to access the
+ * parameters from the rest of the program are defined. */
+
+#define FN_GLOBAL_STRING(fn_name, ptr) \
+ char *fn_name(void) {return expand_vars(*(char **)(ptr) ? *(char **)(ptr) : "");}
+#define FN_GLOBAL_BOOL(fn_name, ptr) \
+ BOOL fn_name(void) {return *(BOOL *)(ptr);}
+#define FN_GLOBAL_CHAR(fn_name, ptr) \
+ char fn_name(void) {return *(char *)(ptr);}
+#define FN_GLOBAL_INTEGER(fn_name, ptr) \
+ int fn_name(void) {return *(int *)(ptr);}
+
+#define FN_LOCAL_STRING(fn_name, val) \
+ char *fn_name(int i) {return expand_vars(LP_SNUM_OK(i) && iSECTION(i).val ? iSECTION(i).val : Vars.l.val ? Vars.l.val : "");}
+#define FN_LOCAL_BOOL(fn_name, val) \
+ BOOL fn_name(int i) {return LP_SNUM_OK(i)? iSECTION(i).val : Vars.l.val;}
+#define FN_LOCAL_CHAR(fn_name, val) \
+ char fn_name(int i) {return LP_SNUM_OK(i)? iSECTION(i).val : Vars.l.val;}
+#define FN_LOCAL_INTEGER(fn_name, val) \
+ int fn_name(int i) {return LP_SNUM_OK(i)? iSECTION(i).val : Vars.l.val;}
+
+FN_GLOBAL_STRING(lp_bind_address, &Vars.g.bind_address)
+FN_GLOBAL_STRING(lp_motd_file, &Vars.g.motd_file)
+FN_GLOBAL_STRING(lp_pid_file, &Vars.g.pid_file)
+FN_GLOBAL_STRING(lp_socket_options, &Vars.g.socket_options)
+
+FN_GLOBAL_INTEGER(lp_listen_backlog, &Vars.g.listen_backlog)
+FN_GLOBAL_INTEGER(lp_rsync_port, &Vars.g.rsync_port)
+
+FN_LOCAL_STRING(lp_auth_users, auth_users)
+FN_LOCAL_STRING(lp_charset, charset)
+FN_LOCAL_STRING(lp_comment, comment)
+FN_LOCAL_STRING(lp_dont_compress, dont_compress)
+FN_LOCAL_STRING(lp_exclude, exclude)
+FN_LOCAL_STRING(lp_exclude_from, exclude_from)
+FN_LOCAL_STRING(lp_filter, filter)
+FN_LOCAL_STRING(lp_gid, gid)
+FN_LOCAL_STRING(lp_hosts_allow, hosts_allow)
+FN_LOCAL_STRING(lp_hosts_deny, hosts_deny)
+FN_LOCAL_STRING(lp_include, include)
+FN_LOCAL_STRING(lp_include_from, include_from)
+FN_LOCAL_STRING(lp_incoming_chmod, incoming_chmod)
+FN_LOCAL_STRING(lp_lock_file, lock_file)
+FN_LOCAL_STRING(lp_log_file, log_file)
+FN_LOCAL_STRING(lp_log_format, log_format)
+FN_LOCAL_STRING(lp_name, name)
+FN_LOCAL_STRING(lp_outgoing_chmod, outgoing_chmod)
+FN_LOCAL_STRING(lp_path, path)
+FN_LOCAL_STRING(lp_postxfer_exec, postxfer_exec)
+FN_LOCAL_STRING(lp_prexfer_exec, prexfer_exec)
+FN_LOCAL_STRING(lp_refuse_options, refuse_options)
+FN_LOCAL_STRING(lp_secrets_file, secrets_file)
+FN_LOCAL_STRING(lp_temp_dir, temp_dir)
+FN_LOCAL_STRING(lp_uid, uid)
+
+FN_LOCAL_INTEGER(lp_max_connections, max_connections)
+FN_LOCAL_INTEGER(lp_max_verbosity, max_verbosity)
+FN_LOCAL_INTEGER(lp_syslog_facility, syslog_facility)
+FN_LOCAL_INTEGER(lp_timeout, timeout)
+
+FN_LOCAL_BOOL(lp_fake_super, fake_super)
+FN_LOCAL_BOOL(lp_forward_lookup, forward_lookup)
+FN_LOCAL_BOOL(lp_ignore_errors, ignore_errors)
+FN_LOCAL_BOOL(lp_ignore_nonreadable, ignore_nonreadable)
+FN_LOCAL_BOOL(lp_list, list)
+FN_LOCAL_BOOL(lp_munge_symlinks, munge_symlinks)
+FN_LOCAL_BOOL(lp_numeric_ids, numeric_ids)
+FN_LOCAL_BOOL(lp_read_only, read_only)
+FN_LOCAL_BOOL(lp_reverse_lookup, reverse_lookup)
+FN_LOCAL_BOOL(lp_strict_modes, strict_modes)
+FN_LOCAL_BOOL(lp_transfer_logging, transfer_logging)
+FN_LOCAL_BOOL(lp_use_chroot, use_chroot)
+FN_LOCAL_BOOL(lp_write_only, write_only)
+
+/* Assign a copy of v to *s. Handles NULL strings. We don't worry
+ * about overwriting a malloc'd string because the long-running
+ * (port-listening) daemon only loads the config file once, and the
+ * per-job (forked or xinitd-ran) daemon only re-reads the file at
+ * the start, so any lost memory is inconsequential. */
+static inline void string_set(char **s, const char *v)
+{
+ if (!v)
+ *s = NULL;
+ else if (!(*s = strdup(v)))
+ out_of_memory("string_set");
+}
+
+/* Copy the local_vars, strdup'ing any strings. NOTE: this depends on
+ * the structure starting with a contiguous list of the char* variables,
+ * and having an accurate count in the LOCAL_STRING_COUNT() macro. */
+static void copy_section(local_vars *psectionDest, local_vars *psectionSource)
+{
+ int count = LOCAL_STRING_COUNT();
+ char **strings = (char**)psectionDest;
+
+ memcpy(psectionDest, psectionSource, sizeof psectionDest[0]);
+ while (count--) {
+ if (strings[count] && !(strings[count] = strdup(strings[count])))
+ out_of_memory("copy_section");
+ }
+}
+
+/* Initialise a section to the defaults. */
+static void init_section(local_vars *psection)
+{
+ memset(psection, 0, sizeof (local_vars));
+ copy_section(psection, &Vars.l);
+}
+
+/* Do a case-insensitive, whitespace-ignoring string compare. */
+static int strwicmp(char *psz1, char *psz2)
+{
+ /* if BOTH strings are NULL, return TRUE, if ONE is NULL return */
+ /* appropriate value. */
+ if (psz1 == psz2)
+ return 0;
+
+ if (psz1 == NULL)
+ return -1;
+
+ if (psz2 == NULL)
+ return 1;
+
+ /* sync the strings on first non-whitespace */
+ while (1) {
+ while (isSpace(psz1))
+ psz1++;
+ while (isSpace(psz2))
+ psz2++;
+ if (toUpper(psz1) != toUpper(psz2) || *psz1 == '\0' || *psz2 == '\0')
+ break;
+ psz1++;
+ psz2++;
+ }
+ return *psz1 - *psz2;
+}
+
+/* Find a section by name. Otherwise works like get_section. */
+static int getsectionbyname(char *name)
+{
+ int i;
+
+ for (i = section_list.count - 1; i >= 0; i--) {
+ if (strwicmp(iSECTION(i).name, name) == 0)
+ break;
+ }
+
+ return i;
+}
+
+/* Add a new section to the sections array w/the default values. */
+static int add_a_section(char *name)
+{
+ int i;
+ local_vars *s;
+
+ /* it might already exist */
+ if (name) {
+ i = getsectionbyname(name);
+ if (i >= 0)
+ return i;
+ }
+
+ i = section_list.count;
+ s = EXPAND_ITEM_LIST(§ion_list, local_vars, 2);
+
+ init_section(s);
+ if (name)
+ string_set(&s->name, name);
+
+ return i;
+}
+
+/* Map a parameter's string representation to something we can use.
+ * Returns False if the parameter string is not recognised, else TRUE. */
+static int map_parameter(char *parmname)
+{
+ int iIndex;
+
+ if (*parmname == '-')
+ return -1;
+
+ for (iIndex = 0; parm_table[iIndex].label; iIndex++) {
+ if (strwicmp(parm_table[iIndex].label, parmname) == 0)
+ return iIndex;
+ }
+
+ rprintf(FLOG, "Unknown Parameter encountered: \"%s\"\n", parmname);
+ return -1;
+}
+
+/* Set a boolean variable from the text value stored in the passed string.
+ * Returns True in success, False if the passed string does not correctly
+ * represent a boolean. */
+static BOOL set_boolean(BOOL *pb, char *parmvalue)
+{
+ if (strwicmp(parmvalue, "yes") == 0
+ || strwicmp(parmvalue, "true") == 0
+ || strwicmp(parmvalue, "1") == 0)
+ *pb = True;
+ else if (strwicmp(parmvalue, "no") == 0
+ || strwicmp(parmvalue, "False") == 0
+ || strwicmp(parmvalue, "0") == 0)
+ *pb = False;
+ else {
+ rprintf(FLOG, "Badly formed boolean in configuration file: \"%s\".\n", parmvalue);
+ return False;
+ }
+ return True;
+}
+
+/* Process a parameter. */
+static BOOL do_parameter(char *parmname, char *parmvalue)
+{
+ int parmnum, i;
+ void *parm_ptr; /* where we are going to store the result */
+ void *def_ptr;
+ char *cp;
+
+ parmnum = map_parameter(parmname);
+
+ if (parmnum < 0) {
+ rprintf(FLOG, "IGNORING unknown parameter \"%s\"\n", parmname);
+ return True;
+ }
+
+ def_ptr = parm_table[parmnum].ptr;
+
+ if (bInGlobalSection)
+ parm_ptr = def_ptr;
+ else {
+ if (parm_table[parmnum].class == P_GLOBAL) {
+ rprintf(FLOG, "Global parameter %s found in module section!\n", parmname);
+ return True;
+ }
+ parm_ptr = SECTION_PTR(&iSECTION(iSectionIndex), def_ptr);
+ }
+
+ /* now switch on the type of variable it is */
+ switch (parm_table[parmnum].type) {
+ case P_PATH:
+ case P_STRING:
+ /* delay expansion of vars */
+ break;
+ default:
+ /* expand any %VARS% now */
+ parmvalue = expand_vars(parmvalue);
+ break;
+ }
+
+ switch (parm_table[parmnum].type) {
+ case P_BOOL:
+ set_boolean(parm_ptr, parmvalue);
+ break;
+
+ case P_BOOLREV:
+ set_boolean(parm_ptr, parmvalue);
+ *(BOOL *)parm_ptr = ! *(BOOL *)parm_ptr;
+ break;
+
+ case P_INTEGER:
+ *(int *)parm_ptr = atoi(parmvalue);
+ break;
+
+ case P_CHAR:
+ *(char *)parm_ptr = *parmvalue;
+ break;
+
+ case P_OCTAL:
+ sscanf(parmvalue, "%o", (int *)parm_ptr);
+ break;
+
+ case P_PATH:
+ string_set(parm_ptr, parmvalue);
+ if ((cp = *(char**)parm_ptr) != NULL) {
+ int len = strlen(cp);
+ while (len > 1 && cp[len-1] == '/') len--;
+ cp[len] = '\0';
+ }
+ break;
+
+ case P_STRING:
+ string_set(parm_ptr, parmvalue);
+ break;
+
+ case P_ENUM:
+ for (i=0; parm_table[parmnum].enum_list[i].name; i++) {
+ if (strequal(parmvalue, parm_table[parmnum].enum_list[i].name)) {
+ *(int *)parm_ptr = parm_table[parmnum].enum_list[i].value;
+ break;
+ }
+ }
+ if (!parm_table[parmnum].enum_list[i].name) {
+ if (atoi(parmvalue) > 0)
+ *(int *)parm_ptr = atoi(parmvalue);
+ }
+ break;
+ }
+
+ return True;
+}
+
+/* Process a new section (rsync module).
+ * Returns True on success, False on failure. */
+static BOOL do_section(char *sectionname)
+{
+ BOOL isglobal;
+
+ if (*sectionname == ']') { /* A special push/pop/reset directive from params.c */
+ bInGlobalSection = 1;
+ if (strcmp(sectionname+1, "push") == 0) {
+ all_vars *vp = EXPAND_ITEM_LIST(&Vars_stack, all_vars, 2);
+ memcpy(vp, &Vars, sizeof Vars);
+ } else if (strcmp(sectionname+1, "pop") == 0
+ || strcmp(sectionname+1, "reset") == 0) {
+ all_vars *vp = ((all_vars*)Vars_stack.items) + Vars_stack.count - 1;
+ if (!Vars_stack.count)
+ return False;
+ memcpy(&Vars, vp, sizeof Vars);
+ if (sectionname[1] == 'p')
+ Vars_stack.count--;
+ } else
+ return False;
+ return True;
+ }
+
+ isglobal = strwicmp(sectionname, GLOBAL_NAME) == 0;
+
+ /* At the end of the global section, add any --dparam items. */
+ if (bInGlobalSection && !isglobal) {
+ if (!section_list.count)
+ set_dparams(0);
+ }
+
+ /* if we've just struck a global section, note the fact. */
+ bInGlobalSection = isglobal;
+
+ /* check for multiple global sections */
+ if (bInGlobalSection)
+ return True;
+
+#if 0
+ /* If we have a current section, tidy it up before moving on. */
+ if (iSectionIndex >= 0) {
+ /* Add any tidy work as needed ... */
+ if (problem)
+ return False;
+ }
+#endif
+
+ if (strchr(sectionname, '/') != NULL) {
+ rprintf(FLOG, "Warning: invalid section name in configuration file: %s\n", sectionname);
+ return False;
+ }
+
+ if ((iSectionIndex = add_a_section(sectionname)) < 0) {
+ rprintf(FLOG, "Failed to add a new module\n");
+ bInGlobalSection = True;
+ return False;
+ }
+
+ return True;
+}
+
+/* Load the modules from the config file. Return True on success,
+ * False on failure. */
+int lp_load(char *pszFname, int globals_only)
+{
+ bInGlobalSection = True;
+
+ reset_all_vars();
+
+ /* We get sections first, so have to start 'behind' to make up. */
+ iSectionIndex = -1;
+ return pm_process(pszFname, globals_only ? NULL : do_section, do_parameter);
+}
+
+BOOL set_dparams(int syntax_check_only)
+{
+ char *equal, *val, **params = dparam_list.items;
+ unsigned j;
+
+ for (j = 0; j < dparam_list.count; j++) {
+ equal = strchr(params[j], '='); /* options.c verified this */
+ *equal = '\0';
+ if (syntax_check_only) {
+ if (map_parameter(params[j]) < 0) {
+ rprintf(FERROR, "Unknown parameter \"%s\"\n", params[j]);
+ *equal = '=';
+ return False;
+ }
+ } else {
+ for (val = equal+1; isSpace(val); val++) {}
+ do_parameter(params[j], val);
+ }
+ *equal = '=';
+ }
+
+ return True;
+}
+
+/* Return the max number of modules (sections). */
+int lp_num_modules(void)
+{
+ return section_list.count;
+}
+
+/* Return the number of the module with the given name, or -1 if it doesn't
+ * exist. Note that this is a DIFFERENT ANIMAL from the internal function
+ * getsectionbyname()! This works ONLY if all sections have been loaded,
+ * and does not copy the found section. */
+int lp_number(char *name)
+{
+ int i;
+
+ for (i = section_list.count - 1; i >= 0; i--) {
+ if (strcmp(lp_name(i), name) == 0)
+ break;
+ }
+
+ return i;
+}
diff --git a/rsync/log.c b/rsync/log.c
new file mode 100644
index 0000000..6e87a5b
--- /dev/null
+++ b/rsync/log.c
@@ -0,0 +1,896 @@
+/*
+ * Logging and utility functions.
+ *
+ * Copyright (C) 1998-2001 Andrew Tridgell
+ * Copyright (C) 2000-2001 Martin Pool
+ * Copyright (C) 2003-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "itypes.h"
+#include "inums.h"
+
+extern int dry_run;
+extern int am_daemon;
+extern int am_server;
+extern int am_sender;
+extern int am_generator;
+extern int local_server;
+extern int quiet;
+extern int module_id;
+extern int checksum_len;
+extern int allow_8bit_chars;
+extern int protocol_version;
+extern int always_checksum;
+extern int preserve_times;
+extern int msgs2stderr;
+extern int stdout_format_has_i;
+extern int stdout_format_has_o_or_i;
+extern int logfile_format_has_i;
+extern int logfile_format_has_o_or_i;
+extern int receiver_symlink_times;
+extern int64 total_data_written;
+extern int64 total_data_read;
+extern mode_t orig_umask;
+extern char *auth_user;
+extern char *stdout_format;
+extern char *logfile_format;
+extern char *logfile_name;
+#ifdef ICONV_CONST
+extern iconv_t ic_chck;
+#endif
+#ifdef ICONV_OPTION
+extern iconv_t ic_recv;
+#endif
+extern char curr_dir[MAXPATHLEN];
+extern char *full_module_path;
+extern unsigned int module_dirlen;
+extern char sender_file_sum[MAX_DIGEST_LEN];
+extern const char undetermined_hostname[];
+
+static int log_initialised;
+static int logfile_was_closed;
+static FILE *logfile_fp;
+struct stats stats;
+
+int got_xfer_error = 0;
+int output_needs_newline = 0;
+int send_msgs_to_gen = 0;
+
+static int64 initial_data_written;
+static int64 initial_data_read;
+
+struct {
+ int code;
+ char const *name;
+} const rerr_names[] = {
+ { RERR_SYNTAX , "syntax or usage error" },
+ { RERR_PROTOCOL , "protocol incompatibility" },
+ { RERR_FILESELECT , "errors selecting input/output files, dirs" },
+ { RERR_UNSUPPORTED, "requested action not supported" },
+ { RERR_STARTCLIENT, "error starting client-server protocol" },
+ { RERR_SOCKETIO , "error in socket IO" },
+ { RERR_FILEIO , "error in file IO" },
+ { RERR_STREAMIO , "error in rsync protocol data stream" },
+ { RERR_MESSAGEIO , "errors with program diagnostics" },
+ { RERR_IPC , "error in IPC code" },
+ { RERR_CRASHED , "sibling process crashed" },
+ { RERR_TERMINATED , "sibling process terminated abnormally" },
+ { RERR_SIGNAL1 , "received SIGUSR1" },
+ { RERR_SIGNAL , "received SIGINT, SIGTERM, or SIGHUP" },
+ { RERR_WAITCHILD , "waitpid() failed" },
+ { RERR_MALLOC , "error allocating core memory buffers" },
+ { RERR_PARTIAL , "some files/attrs were not transferred (see previous errors)" },
+ { RERR_VANISHED , "some files vanished before they could be transferred" },
+ { RERR_DEL_LIMIT , "the --max-delete limit stopped deletions" },
+ { RERR_TIMEOUT , "timeout in data send/receive" },
+ { RERR_CONTIMEOUT , "timeout waiting for daemon connection" },
+ { RERR_CMD_FAILED , "remote shell failed" },
+ { RERR_CMD_KILLED , "remote shell killed" },
+ { RERR_CMD_RUN , "remote command could not be run" },
+ { RERR_CMD_NOTFOUND,"remote command not found" },
+ { 0, NULL }
+};
+
+/*
+ * Map from rsync error code to name, or return NULL.
+ */
+static char const *rerr_name(int code)
+{
+ int i;
+ for (i = 0; rerr_names[i].name; i++) {
+ if (rerr_names[i].code == code)
+ return rerr_names[i].name;
+ }
+ return NULL;
+}
+
+static void logit(int priority, const char *buf)
+{
+ if (logfile_was_closed)
+ logfile_reopen();
+ if (logfile_fp) {
+ fprintf(logfile_fp, "%s [%d] %s", timestring(time(NULL)), (int)getpid(), buf);
+ fflush(logfile_fp);
+ } else {
+ syslog(priority, "%s", buf);
+ }
+}
+
+static void syslog_init()
+{
+ static int been_here = 0;
+ int options = LOG_PID;
+
+ if (been_here)
+ return;
+ been_here = 1;
+
+#ifdef LOG_NDELAY
+ options |= LOG_NDELAY;
+#endif
+
+#ifdef LOG_DAEMON
+ openlog("rsyncd", options, lp_syslog_facility(module_id));
+#else
+ openlog("rsyncd", options);
+#endif
+
+#ifndef LOG_NDELAY
+ logit(LOG_INFO, "rsyncd started\n");
+#endif
+}
+
+static void logfile_open(void)
+{
+ mode_t old_umask = umask(022 | orig_umask);
+ logfile_fp = fopen(logfile_name, "a");
+ umask(old_umask);
+ if (!logfile_fp) {
+ int fopen_errno = errno;
+ /* Rsync falls back to using syslog on failure. */
+ syslog_init();
+ rsyserr(FERROR, fopen_errno,
+ "failed to open log-file %s", logfile_name);
+ rprintf(FINFO, "Ignoring \"log file\" setting.\n");
+ }
+}
+
+void log_init(int restart)
+{
+ if (log_initialised) {
+ if (!restart)
+ return;
+ if (strcmp(logfile_name, lp_log_file(module_id)) != 0) {
+ if (logfile_fp) {
+ fclose(logfile_fp);
+ logfile_fp = NULL;
+ } else
+ closelog();
+ logfile_name = NULL;
+ } else if (*logfile_name)
+ return; /* unchanged, non-empty "log file" names */
+ else if (lp_syslog_facility(-1) != lp_syslog_facility(module_id))
+ closelog();
+ else
+ return; /* unchanged syslog settings */
+ } else
+ log_initialised = 1;
+
+ /* This looks pointless, but it is needed in order for the
+ * C library on some systems to fetch the timezone info
+ * before the chroot. */
+ timestring(time(NULL));
+
+ /* Optionally use a log file instead of syslog. (Non-daemon
+ * rsyncs will have already set logfile_name, as needed.) */
+ if (am_daemon && !logfile_name)
+ logfile_name = lp_log_file(module_id);
+ if (logfile_name && *logfile_name)
+ logfile_open();
+ else
+ syslog_init();
+}
+
+void logfile_close(void)
+{
+ if (logfile_fp) {
+ logfile_was_closed = 1;
+ fclose(logfile_fp);
+ logfile_fp = NULL;
+ }
+}
+
+void logfile_reopen(void)
+{
+ if (logfile_was_closed) {
+ logfile_was_closed = 0;
+ logfile_open();
+ }
+}
+
+static void filtered_fwrite(FILE *f, const char *buf, int len, int use_isprint)
+{
+ const char *s, *end = buf + len;
+ for (s = buf; s < end; s++) {
+ if ((s < end - 4
+ && *s == '\\' && s[1] == '#'
+ && isDigit(s + 2)
+ && isDigit(s + 3)
+ && isDigit(s + 4))
+ || (*s != '\t'
+ && ((use_isprint && !isPrint(s))
+ || *(uchar*)s < ' '))) {
+ if (s != buf && fwrite(buf, s - buf, 1, f) != 1)
+ exit_cleanup(RERR_MESSAGEIO);
+ fprintf(f, "\\#%03o", *(uchar*)s);
+ buf = s + 1;
+ }
+ }
+ if (buf != end && fwrite(buf, end - buf, 1, f) != 1)
+ exit_cleanup(RERR_MESSAGEIO);
+}
+
+/* this is the underlying (unformatted) rsync debugging function. Call
+ * it with FINFO, FERROR_*, FWARNING, FLOG, or FCLIENT. Note: recursion
+ * can happen with certain fatal conditions. */
+void rwrite(enum logcode code, const char *buf, int len, int is_utf8)
+{
+ int trailing_CR_or_NL;
+ FILE *f = msgs2stderr ? stderr : stdout;
+#ifdef ICONV_OPTION
+ iconv_t ic = is_utf8 && ic_recv != (iconv_t)-1 ? ic_recv : ic_chck;
+#else
+#ifdef ICONV_CONST
+ iconv_t ic = ic_chck;
+#endif
+#endif
+
+ if (len < 0)
+ exit_cleanup(RERR_MESSAGEIO);
+
+ if (msgs2stderr) {
+ if (!am_daemon) {
+ if (code == FLOG)
+ return;
+ goto output_msg;
+ }
+ if (code == FCLIENT)
+ return;
+ code = FLOG;
+ } else if (send_msgs_to_gen) {
+ assert(!is_utf8);
+ /* Pass the message to our sibling in native charset. */
+ send_msg((enum msgcode)code, buf, len, 0);
+ return;
+ }
+
+ if (code == FERROR_SOCKET) /* This gets simplified for a non-sibling. */
+ code = FERROR;
+ else if (code == FERROR_UTF8) {
+ is_utf8 = 1;
+ code = FERROR;
+ }
+
+ if (code == FCLIENT)
+ code = FINFO;
+ else if (am_daemon || logfile_name) {
+ static int in_block;
+ char msg[2048];
+ int priority = code == FINFO || code == FLOG ? LOG_INFO : LOG_WARNING;
+
+ if (in_block)
+ return;
+ in_block = 1;
+ if (!log_initialised)
+ log_init(0);
+ strlcpy(msg, buf, MIN((int)sizeof msg, len + 1));
+ logit(priority, msg);
+ in_block = 0;
+
+ if (code == FLOG || (am_daemon && !am_server))
+ return;
+ } else if (code == FLOG)
+ return;
+
+ if (quiet && code == FINFO)
+ return;
+
+ if (am_server) {
+ enum msgcode msg = (enum msgcode)code;
+ if (protocol_version < 30) {
+ if (msg == MSG_ERROR)
+ msg = MSG_ERROR_XFER;
+ else if (msg == MSG_WARNING)
+ msg = MSG_INFO;
+ }
+ /* Pass the message to the non-server side. */
+ if (send_msg(msg, buf, len, !is_utf8))
+ return;
+ if (am_daemon) {
+ /* TODO: can we send the error to the user somehow? */
+ return;
+ }
+ f = stderr;
+ }
+
+output_msg:
+ switch (code) {
+ case FERROR_XFER:
+ got_xfer_error = 1;
+ /* FALL THROUGH */
+ case FERROR:
+ case FERROR_UTF8:
+ case FERROR_SOCKET:
+ case FWARNING:
+ f = stderr;
+ break;
+ case FLOG:
+ case FINFO:
+ case FCLIENT:
+ break;
+ default:
+ fprintf(stderr, "Unknown logcode in rwrite(): %d [%s]\n", (int)code, who_am_i());
+ exit_cleanup(RERR_MESSAGEIO);
+ }
+
+ if (output_needs_newline) {
+ fputc('\n', f);
+ output_needs_newline = 0;
+ }
+
+ trailing_CR_or_NL = len && (buf[len-1] == '\n' || buf[len-1] == '\r')
+ ? buf[--len] : 0;
+
+ if (len && buf[0] == '\r') {
+ fputc('\r', f);
+ buf++;
+ len--;
+ }
+
+#ifdef ICONV_CONST
+ if (ic != (iconv_t)-1) {
+ xbuf outbuf, inbuf;
+ char convbuf[1024];
+ int ierrno;
+
+ INIT_CONST_XBUF(outbuf, convbuf);
+ INIT_XBUF(inbuf, (char*)buf, len, (size_t)-1);
+
+ while (inbuf.len) {
+ iconvbufs(ic, &inbuf, &outbuf, inbuf.pos ? 0 : ICB_INIT);
+ ierrno = errno;
+ if (outbuf.len) {
+ filtered_fwrite(f, convbuf, outbuf.len, 0);
+ outbuf.len = 0;
+ }
+ if (!ierrno || ierrno == E2BIG)
+ continue;
+ fprintf(f, "\\#%03o", CVAL(inbuf.buf, inbuf.pos++));
+ inbuf.len--;
+ }
+ } else
+#endif
+ filtered_fwrite(f, buf, len, !allow_8bit_chars);
+
+ if (trailing_CR_or_NL) {
+ fputc(trailing_CR_or_NL, f);
+ fflush(f);
+ }
+}
+
+/* This is the rsync debugging function. Call it with FINFO, FERROR_*,
+ * FWARNING, FLOG, or FCLIENT. */
+void rprintf(enum logcode code, const char *format, ...)
+{
+ va_list ap;
+ char buf[BIGPATHBUFLEN];
+ size_t len;
+
+ va_start(ap, format);
+ len = vsnprintf(buf, sizeof buf, format, ap);
+ va_end(ap);
+
+ /* Deal with buffer overruns. Instead of panicking, just
+ * truncate the resulting string. (Note that configure ensures
+ * that we have a vsnprintf() that doesn't ever return -1.) */
+ if (len > sizeof buf - 1) {
+ static const char ellipsis[] = "[...]";
+
+ /* Reset length, and zero-terminate the end of our buffer */
+ len = sizeof buf - 1;
+ buf[len] = '\0';
+
+ /* Copy the ellipsis to the end of the string, but give
+ * us one extra character:
+ *
+ * v--- null byte at buf[sizeof buf - 1]
+ * abcdefghij0
+ * -> abcd[...]00 <-- now two null bytes at end
+ *
+ * If the input format string has a trailing newline,
+ * we copy it into that extra null; if it doesn't, well,
+ * all we lose is one byte. */
+ memcpy(buf+len-sizeof ellipsis, ellipsis, sizeof ellipsis);
+ if (format[strlen(format)-1] == '\n') {
+ buf[len-1] = '\n';
+ }
+ }
+
+ rwrite(code, buf, len, 0);
+}
+
+/* This is like rprintf, but it also tries to print some
+ * representation of the error code. Normally errcode = errno.
+ *
+ * Unlike rprintf, this always adds a newline and there should not be
+ * one in the format string.
+ *
+ * Note that since strerror might involve dynamically loading a
+ * message catalog we need to call it once before chroot-ing. */
+void rsyserr(enum logcode code, int errcode, const char *format, ...)
+{
+ va_list ap;
+ char buf[BIGPATHBUFLEN];
+ size_t len;
+
+ strlcpy(buf, RSYNC_NAME ": ", sizeof buf);
+ len = (sizeof RSYNC_NAME ": ") - 1;
+
+ va_start(ap, format);
+ len += vsnprintf(buf + len, sizeof buf - len, format, ap);
+ va_end(ap);
+
+ if (len < sizeof buf) {
+ len += snprintf(buf + len, sizeof buf - len,
+ ": %s (%d)\n", strerror(errcode), errcode);
+ }
+ if (len >= sizeof buf)
+ exit_cleanup(RERR_MESSAGEIO);
+
+ rwrite(code, buf, len, 0);
+}
+
+void rflush(enum logcode code)
+{
+ FILE *f;
+
+ if (am_daemon || code == FLOG)
+ return;
+
+ if (!am_server && (code == FINFO || code == FCLIENT))
+ f = stdout;
+ else
+ f = stderr;
+
+ fflush(f);
+}
+
+void remember_initial_stats(void)
+{
+ initial_data_read = total_data_read;
+ initial_data_written = total_data_written;
+}
+
+/* A generic logging routine for send/recv, with parameter substitiution. */
+static void log_formatted(enum logcode code, const char *format, const char *op,
+ struct file_struct *file, const char *fname, int iflags,
+ const char *hlink)
+{
+ char buf[MAXPATHLEN+1024], buf2[MAXPATHLEN], fmt[32];
+ char *p, *s, *c;
+ const char *n;
+ size_t len, total;
+ int64 b;
+
+ *fmt = '%';
+
+ /* We expand % codes one by one in place in buf. We don't
+ * copy in the terminating null of the inserted strings, but
+ * rather keep going until we reach the null of the format. */
+ total = strlcpy(buf, format, sizeof buf);
+ if (total > MAXPATHLEN) {
+ rprintf(FERROR, "log-format string is WAY too long!\n");
+ exit_cleanup(RERR_MESSAGEIO);
+ }
+ buf[total++] = '\n';
+ buf[total] = '\0';
+
+ for (p = buf; (p = strchr(p, '%')) != NULL; ) {
+ int humanize = 0;
+ s = p++;
+ c = fmt + 1;
+ while (*p == '\'') {
+ humanize++;
+ p++;
+ }
+ if (*p == '-')
+ *c++ = *p++;
+ while (isDigit(p) && c - fmt < (int)(sizeof fmt) - 8)
+ *c++ = *p++;
+ while (*p == '\'') {
+ humanize++;
+ p++;
+ }
+ if (!*p)
+ break;
+ *c = '\0';
+ n = NULL;
+
+ /* Note for %h and %a: it doesn't matter what fd we pass to
+ * client_{name,addr} because rsync_module will already have
+ * forced the answer to be cached (assuming, of course, for %h
+ * that lp_reverse_lookup(module_id) is true). */
+ switch (*p) {
+ case 'h':
+ if (am_daemon) {
+ n = lp_reverse_lookup(module_id)
+ ? client_name(0) : undetermined_hostname;
+ }
+ break;
+ case 'a':
+ if (am_daemon)
+ n = client_addr(0);
+ break;
+ case 'l':
+ strlcat(fmt, "s", sizeof fmt);
+ snprintf(buf2, sizeof buf2, fmt,
+ do_big_num(F_LENGTH(file), humanize, NULL));
+ n = buf2;
+ break;
+ case 'U':
+ strlcat(fmt, "u", sizeof fmt);
+ snprintf(buf2, sizeof buf2, fmt,
+ uid_ndx ? F_OWNER(file) : 0);
+ n = buf2;
+ break;
+ case 'G':
+ if (!gid_ndx || file->flags & FLAG_SKIP_GROUP)
+ n = "DEFAULT";
+ else {
+ strlcat(fmt, "u", sizeof fmt);
+ snprintf(buf2, sizeof buf2, fmt,
+ F_GROUP(file));
+ n = buf2;
+ }
+ break;
+ case 'p':
+ strlcat(fmt, "d", sizeof fmt);
+ snprintf(buf2, sizeof buf2, fmt, (int)getpid());
+ n = buf2;
+ break;
+ case 'M':
+ n = c = timestring(file->modtime);
+ while ((c = strchr(c, ' ')) != NULL)
+ *c = '-';
+ break;
+ case 'B':
+ c = buf2 + MAXPATHLEN - PERMSTRING_SIZE - 1;
+ permstring(c, file->mode);
+ n = c + 1; /* skip the type char */
+ break;
+ case 'o':
+ n = op;
+ break;
+ case 'f':
+ if (fname) {
+ c = f_name_buf();
+ strlcpy(c, fname, MAXPATHLEN);
+ } else
+ c = f_name(file, NULL);
+ if (am_sender && F_PATHNAME(file)) {
+ pathjoin(buf2, sizeof buf2,
+ F_PATHNAME(file), c);
+ clean_fname(buf2, 0);
+ if (fmt[1]) {
+ strlcpy(c, buf2, MAXPATHLEN);
+ n = c;
+ } else
+ n = buf2;
+ } else if (am_daemon && *c != '/') {
+ pathjoin(buf2, sizeof buf2,
+ curr_dir + module_dirlen, c);
+ clean_fname(buf2, 0);
+ if (fmt[1]) {
+ strlcpy(c, buf2, MAXPATHLEN);
+ n = c;
+ } else
+ n = buf2;
+ } else {
+ clean_fname(c, 0);
+ n = c;
+ }
+ if (*n == '/')
+ n++;
+ break;
+ case 'n':
+ if (fname) {
+ c = f_name_buf();
+ strlcpy(c, fname, MAXPATHLEN);
+ } else
+ c = f_name(file, NULL);
+ if (S_ISDIR(file->mode))
+ strlcat(c, "/", MAXPATHLEN);
+ n = c;
+ break;
+ case 'L':
+ if (hlink && *hlink) {
+ n = hlink;
+ strlcpy(buf2, " => ", sizeof buf2);
+ } else if (S_ISLNK(file->mode) && !fname) {
+ n = F_SYMLINK(file);
+ strlcpy(buf2, " -> ", sizeof buf2);
+ } else {
+ n = "";
+ if (!fmt[1])
+ break;
+ strlcpy(buf2, " ", sizeof buf2);
+ }
+ strlcat(fmt, "s", sizeof fmt);
+ snprintf(buf2 + 4, sizeof buf2 - 4, fmt, n);
+ n = buf2;
+ break;
+ case 'm':
+ n = lp_name(module_id);
+ break;
+ case 't':
+ n = timestring(time(NULL));
+ break;
+ case 'P':
+ n = full_module_path;
+ break;
+ case 'u':
+ n = auth_user;
+ break;
+ case 'b':
+ if (!(iflags & ITEM_TRANSFER))
+ b = 0;
+ else if (am_sender)
+ b = total_data_written - initial_data_written;
+ else
+ b = total_data_read - initial_data_read;
+ strlcat(fmt, "s", sizeof fmt);
+ snprintf(buf2, sizeof buf2, fmt,
+ do_big_num(b, humanize, NULL));
+ n = buf2;
+ break;
+ case 'c':
+ if (!(iflags & ITEM_TRANSFER))
+ b = 0;
+ else if (!am_sender)
+ b = total_data_written - initial_data_written;
+ else
+ b = total_data_read - initial_data_read;
+ strlcat(fmt, "s", sizeof fmt);
+ snprintf(buf2, sizeof buf2, fmt,
+ do_big_num(b, humanize, NULL));
+ n = buf2;
+ break;
+ case 'C':
+ if (protocol_version >= 30
+ && (iflags & ITEM_TRANSFER
+ || (always_checksum && S_ISREG(file->mode)))) {
+ const char *sum = iflags & ITEM_TRANSFER
+ ? sender_file_sum : F_SUM(file);
+ n = sum_as_hex(sum);
+ } else {
+ memset(buf2, ' ', checksum_len*2);
+ buf2[checksum_len*2] = '\0';
+ n = buf2;
+ }
+ break;
+ case 'i':
+ if (iflags & ITEM_DELETED) {
+ n = "*deleting ";
+ break;
+ }
+ n = c = buf2 + MAXPATHLEN - 32;
+ c[0] = iflags & ITEM_LOCAL_CHANGE
+ ? iflags & ITEM_XNAME_FOLLOWS ? 'h' : 'c'
+ : !(iflags & ITEM_TRANSFER) ? '.'
+ : !local_server && *op == 's' ? '<' : '>';
+ if (S_ISLNK(file->mode)) {
+ c[1] = 'L';
+ c[3] = '.';
+ c[4] = !(iflags & ITEM_REPORT_TIME) ? '.'
+ : !preserve_times || !receiver_symlink_times
+ || (iflags & ITEM_REPORT_TIMEFAIL) ? 'T' : 't';
+ } else {
+ c[1] = S_ISDIR(file->mode) ? 'd'
+ : IS_SPECIAL(file->mode) ? 'S'
+ : IS_DEVICE(file->mode) ? 'D' : 'f';
+ c[3] = !(iflags & ITEM_REPORT_SIZE) ? '.' : 's';
+ c[4] = !(iflags & ITEM_REPORT_TIME) ? '.'
+ : !preserve_times ? 'T' : 't';
+ }
+ c[2] = !(iflags & ITEM_REPORT_CHANGE) ? '.' : 'c';
+ c[5] = !(iflags & ITEM_REPORT_PERMS) ? '.' : 'p';
+ c[6] = !(iflags & ITEM_REPORT_OWNER) ? '.' : 'o';
+ c[7] = !(iflags & ITEM_REPORT_GROUP) ? '.' : 'g';
+ c[8] = !(iflags & ITEM_REPORT_ATIME) ? '.' : 'u';
+ c[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a';
+ c[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x';
+ c[11] = '\0';
+
+ if (iflags & (ITEM_IS_NEW|ITEM_MISSING_DATA)) {
+ char ch = iflags & ITEM_IS_NEW ? '+' : '?';
+ int i;
+ for (i = 2; c[i]; i++)
+ c[i] = ch;
+ } else if (c[0] == '.' || c[0] == 'h' || c[0] == 'c') {
+ int i;
+ for (i = 2; c[i]; i++) {
+ if (c[i] != '.')
+ break;
+ }
+ if (!c[i]) {
+ for (i = 2; c[i]; i++)
+ c[i] = ' ';
+ }
+ }
+ break;
+ }
+
+ /* "n" is the string to be inserted in place of this % code. */
+ if (!n)
+ continue;
+ if (n != buf2 && fmt[1]) {
+ strlcat(fmt, "s", sizeof fmt);
+ snprintf(buf2, sizeof buf2, fmt, n);
+ n = buf2;
+ }
+ len = strlen(n);
+
+ /* Subtract the length of the escape from the string's size. */
+ total -= p - s + 1;
+
+ if (len + total >= (size_t)sizeof buf) {
+ rprintf(FERROR,
+ "buffer overflow expanding %%%c -- exiting\n",
+ p[0]);
+ exit_cleanup(RERR_MESSAGEIO);
+ }
+
+ /* Shuffle the rest of the string along to make space for n */
+ if (len != (size_t)(p - s + 1))
+ memmove(s + len, p + 1, total - (s - buf) + 1);
+ total += len;
+
+ /* Insert the contents of string "n", but NOT its null. */
+ if (len)
+ memcpy(s, n, len);
+
+ /* Skip over inserted string; continue looking */
+ p = s + len;
+ }
+
+ rwrite(code, buf, total, 0);
+}
+
+/* Return 1 if the format escape is in the log-format string (e.g. look for
+ * the 'b' in the "%9b" format escape). */
+int log_format_has(const char *format, char esc)
+{
+ const char *p;
+
+ if (!format)
+ return 0;
+
+ for (p = format; (p = strchr(p, '%')) != NULL; ) {
+ for (p++; *p == '\''; p++) {} /*SHARED ITERATOR*/
+ if (*p == '-')
+ p++;
+ while (isDigit(p))
+ p++;
+ while (*p == '\'') p++;
+ if (!*p)
+ break;
+ if (*p == esc)
+ return 1;
+ }
+ return 0;
+}
+
+/* Log the transfer of a file. If the code is FCLIENT, the output just goes
+ * to stdout. If it is FLOG, it just goes to the log file. Otherwise we
+ * output to both. */
+void log_item(enum logcode code, struct file_struct *file, int iflags, const char *hlink)
+{
+ const char *s_or_r = am_sender ? "send" : "recv";
+
+ if (code != FLOG && stdout_format && !am_server)
+ log_formatted(FCLIENT, stdout_format, s_or_r, file, NULL, iflags, hlink);
+ if (code != FCLIENT && logfile_format && *logfile_format)
+ log_formatted(FLOG, logfile_format, s_or_r, file, NULL, iflags, hlink);
+}
+
+void maybe_log_item(struct file_struct *file, int iflags, int itemizing,
+ const char *buf)
+{
+ int significant_flags = iflags & SIGNIFICANT_ITEM_FLAGS;
+ int see_item = itemizing && (significant_flags || *buf
+ || stdout_format_has_i > 1 || (INFO_GTE(NAME, 2) && stdout_format_has_i));
+ int local_change = iflags & ITEM_LOCAL_CHANGE && significant_flags;
+ if (am_server) {
+ if (logfile_name && !dry_run && see_item
+ && (significant_flags || logfile_format_has_i))
+ log_item(FLOG, file, iflags, buf);
+ } else if (see_item || local_change || *buf
+ || (S_ISDIR(file->mode) && significant_flags)) {
+ enum logcode code = significant_flags || logfile_format_has_i ? FINFO : FCLIENT;
+ log_item(code, file, iflags, buf);
+ }
+}
+
+void log_delete(const char *fname, int mode)
+{
+ static struct {
+ union file_extras ex[4]; /* just in case... */
+ struct file_struct file;
+ } x; /* Zero-initialized due to static declaration. */
+ int len = strlen(fname);
+ const char *fmt;
+
+ x.file.mode = mode;
+
+ if (am_server && protocol_version >= 29 && len < MAXPATHLEN) {
+ if (S_ISDIR(mode))
+ len++; /* directories include trailing null */
+ send_msg(MSG_DELETED, fname, len, am_generator);
+ } else if (!INFO_GTE(DEL, 1) && !stdout_format)
+ ;
+ else {
+ fmt = stdout_format_has_o_or_i ? stdout_format : "deleting %n";
+ log_formatted(FCLIENT, fmt, "del.", &x.file, fname, ITEM_DELETED, NULL);
+ }
+
+ if (!logfile_name || dry_run || !logfile_format)
+ return;
+
+ fmt = logfile_format_has_o_or_i ? logfile_format : "deleting %n";
+ log_formatted(FLOG, fmt, "del.", &x.file, fname, ITEM_DELETED, NULL);
+}
+
+/*
+ * Called when the transfer is interrupted for some reason.
+ *
+ * Code is one of the RERR_* codes from errcode.h, or terminating
+ * successfully.
+ */
+void log_exit(int code, const char *file, int line)
+{
+ if (code == 0) {
+ rprintf(FLOG,"sent %s bytes received %s bytes total size %s\n",
+ big_num(stats.total_written),
+ big_num(stats.total_read),
+ big_num(stats.total_size));
+ } else if (am_server != 2) {
+ const char *name;
+
+ name = rerr_name(code);
+ if (!name)
+ name = "unexplained error";
+
+ /* VANISHED is not an error, only a warning */
+ if (code == RERR_VANISHED) {
+ rprintf(FWARNING, "rsync warning: %s (code %d) at %s(%d) [%s=%s]\n",
+ name, code, file, line, who_am_i(), RSYNC_VERSION);
+ } else {
+ rprintf(FERROR, "rsync error: %s (code %d) at %s(%d) [%s=%s]\n",
+ name, code, file, line, who_am_i(), RSYNC_VERSION);
+ }
+ }
+}
diff --git a/rsync/main.c b/rsync/main.c
new file mode 100644
index 0000000..e7a13f7
--- /dev/null
+++ b/rsync/main.c
@@ -0,0 +1,1640 @@
+/*
+ * The startup routines, including main(), for rsync.
+ *
+ * Copyright (C) 1996-2001 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2001, 2002 Martin Pool
+ * Copyright (C) 2003-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "inums.h"
+#include "io.h"
+#if defined CONFIG_LOCALE && defined HAVE_LOCALE_H
+#include
+#endif
+
+extern int dry_run;
+extern int list_only;
+extern int io_timeout;
+extern int am_root;
+extern int am_server;
+extern int am_sender;
+extern int am_daemon;
+extern int inc_recurse;
+extern int blocking_io;
+extern int always_checksum;
+extern int remove_source_files;
+extern int output_needs_newline;
+extern int need_messages_from_generator;
+extern int kluge_around_eof;
+extern int got_xfer_error;
+extern int msgs2stderr;
+extern int module_id;
+extern int read_only;
+extern int copy_links;
+extern int copy_dirlinks;
+extern int copy_unsafe_links;
+extern int keep_dirlinks;
+extern int preserve_hard_links;
+extern int protocol_version;
+extern int file_total;
+extern int recurse;
+extern int xfer_dirs;
+extern int protect_args;
+extern int relative_paths;
+extern int sanitize_paths;
+extern int curr_dir_depth;
+extern int curr_dir_len;
+extern int module_id;
+extern int rsync_port;
+extern int whole_file;
+extern int read_batch;
+extern int write_batch;
+extern int batch_fd;
+extern int sock_f_in;
+extern int sock_f_out;
+extern int filesfrom_fd;
+extern int connect_timeout;
+extern int send_msgs_to_gen;
+extern dev_t filesystem_dev;
+extern pid_t cleanup_child_pid;
+extern size_t bwlimit_writemax;
+extern unsigned int module_dirlen;
+extern BOOL flist_receiving_enabled;
+extern BOOL shutting_down;
+extern int basis_dir_cnt;
+extern struct stats stats;
+extern char *stdout_format;
+extern char *logfile_format;
+extern char *filesfrom_host;
+extern char *partial_dir;
+extern char *dest_option;
+extern char *rsync_path;
+extern char *shell_cmd;
+extern char *batch_name;
+extern char *password_file;
+extern char *backup_dir;
+extern char curr_dir[MAXPATHLEN];
+extern char backup_dir_buf[MAXPATHLEN];
+extern char *basis_dir[MAX_BASIS_DIRS+1];
+extern struct file_list *first_flist;
+extern filter_rule_list daemon_filter_list;
+
+uid_t our_uid;
+gid_t our_gid;
+int am_receiver = 0; /* Only set to 1 after the receiver/generator fork. */
+int am_generator = 0; /* Only set to 1 after the receiver/generator fork. */
+int local_server = 0;
+int daemon_over_rsh = 0;
+mode_t orig_umask = 0;
+int batch_gen_fd = -1;
+int sender_keeps_checksum = 0;
+
+/* There's probably never more than at most 2 outstanding child processes,
+ * but set it higher, just in case. */
+#define MAXCHILDPROCS 7
+
+#ifdef HAVE_SIGACTION
+# ifdef HAVE_SIGPROCMASK
+# define SIGACTMASK(n,h) SIGACTION(n,h), sigaddset(&sigmask,(n))
+# else
+# define SIGACTMASK(n,h) SIGACTION(n,h)
+# endif
+static struct sigaction sigact;
+#endif
+
+struct pid_status {
+ pid_t pid;
+ int status;
+} pid_stat_table[MAXCHILDPROCS];
+
+static time_t starttime, endtime;
+static int64 total_read, total_written;
+
+static void show_malloc_stats(void);
+
+/* Works like waitpid(), but if we already harvested the child pid in our
+ * remember_children(), we succeed instead of returning an error. */
+pid_t wait_process(pid_t pid, int *status_ptr, int flags)
+{
+ pid_t waited_pid;
+
+ do {
+ waited_pid = waitpid(pid, status_ptr, flags);
+ } while (waited_pid == -1 && errno == EINTR);
+
+ if (waited_pid == -1 && errno == ECHILD) {
+ /* Status of requested child no longer available: check to
+ * see if it was processed by remember_children(). */
+ int cnt;
+ for (cnt = 0; cnt < MAXCHILDPROCS; cnt++) {
+ if (pid == pid_stat_table[cnt].pid) {
+ *status_ptr = pid_stat_table[cnt].status;
+ pid_stat_table[cnt].pid = 0;
+ return pid;
+ }
+ }
+ }
+
+ return waited_pid;
+}
+
+/* Wait for a process to exit, calling io_flush while waiting. */
+static void wait_process_with_flush(pid_t pid, int *exit_code_ptr)
+{
+ pid_t waited_pid;
+ int status;
+
+ while ((waited_pid = wait_process(pid, &status, WNOHANG)) == 0) {
+ msleep(20);
+ io_flush(FULL_FLUSH);
+ }
+
+ /* TODO: If the child exited on a signal, then log an
+ * appropriate error message. Perhaps we should also accept a
+ * message describing the purpose of the child. Also indicate
+ * this to the caller so that they know something went wrong. */
+ if (waited_pid < 0) {
+ rsyserr(FERROR, errno, "waitpid");
+ *exit_code_ptr = RERR_WAITCHILD;
+ } else if (!WIFEXITED(status)) {
+#ifdef WCOREDUMP
+ if (WCOREDUMP(status))
+ *exit_code_ptr = RERR_CRASHED;
+ else
+#endif
+ if (WIFSIGNALED(status))
+ *exit_code_ptr = RERR_TERMINATED;
+ else
+ *exit_code_ptr = RERR_WAITCHILD;
+ } else
+ *exit_code_ptr = WEXITSTATUS(status);
+}
+
+void write_del_stats(int f)
+{
+ if (read_batch)
+ write_int(f, NDX_DEL_STATS);
+ else
+ write_ndx(f, NDX_DEL_STATS);
+ write_varint(f, stats.deleted_files - stats.deleted_dirs
+ - stats.deleted_symlinks - stats.deleted_devices
+ - stats.deleted_specials);
+ write_varint(f, stats.deleted_dirs);
+ write_varint(f, stats.deleted_symlinks);
+ write_varint(f, stats.deleted_devices);
+ write_varint(f, stats.deleted_specials);
+}
+
+void read_del_stats(int f)
+{
+ stats.deleted_files = read_varint(f);
+ stats.deleted_files += stats.deleted_dirs = read_varint(f);
+ stats.deleted_files += stats.deleted_symlinks = read_varint(f);
+ stats.deleted_files += stats.deleted_devices = read_varint(f);
+ stats.deleted_files += stats.deleted_specials = read_varint(f);
+}
+
+/* This function gets called from all 3 processes. We want the client side
+ * to actually output the text, but the sender is the only process that has
+ * all the stats we need. So, if we're a client sender, we do the report.
+ * If we're a server sender, we write the stats on the supplied fd. If
+ * we're the client receiver we read the stats from the supplied fd and do
+ * the report. All processes might also generate a set of debug stats, if
+ * the verbose level is high enough (this is the only thing that the
+ * generator process and the server receiver ever do here). */
+static void handle_stats(int f)
+{
+ endtime = time(NULL);
+
+ /* Cache two stats because the read/write code can change it. */
+ total_read = stats.total_read;
+ total_written = stats.total_written;
+
+ if (INFO_GTE(STATS, 3)) {
+ /* These come out from every process */
+ show_malloc_stats();
+ show_flist_stats();
+ }
+
+ if (am_generator)
+ return;
+
+ if (am_daemon) {
+ if (f == -1 || !am_sender)
+ return;
+ }
+
+ if (am_server) {
+ if (am_sender) {
+ write_varlong30(f, total_read, 3);
+ write_varlong30(f, total_written, 3);
+ write_varlong30(f, stats.total_size, 3);
+ if (protocol_version >= 29) {
+ write_varlong30(f, stats.flist_buildtime, 3);
+ write_varlong30(f, stats.flist_xfertime, 3);
+ }
+ }
+ return;
+ }
+
+ /* this is the client */
+
+ if (f < 0 && !am_sender) /* e.g. when we got an empty file list. */
+ ;
+ else if (!am_sender) {
+ /* Read the first two in opposite order because the meaning of
+ * read/write swaps when switching from sender to receiver. */
+ total_written = read_varlong30(f, 3);
+ total_read = read_varlong30(f, 3);
+ stats.total_size = read_varlong30(f, 3);
+ if (protocol_version >= 29) {
+ stats.flist_buildtime = read_varlong30(f, 3);
+ stats.flist_xfertime = read_varlong30(f, 3);
+ }
+ } else if (write_batch) {
+ /* The --read-batch process is going to be a client
+ * receiver, so we need to give it the stats. */
+ write_varlong30(batch_fd, total_read, 3);
+ write_varlong30(batch_fd, total_written, 3);
+ write_varlong30(batch_fd, stats.total_size, 3);
+ if (protocol_version >= 29) {
+ write_varlong30(batch_fd, stats.flist_buildtime, 3);
+ write_varlong30(batch_fd, stats.flist_xfertime, 3);
+ }
+ }
+}
+
+static void output_itemized_counts(const char *prefix, int *counts)
+{
+ static char *labels[] = { "reg", "dir", "link", "dev", "special" };
+ char buf[1024], *pre = " (";
+ int j, len = 0;
+ int total = counts[0];
+ if (total) {
+ counts[0] -= counts[1] + counts[2] + counts[3] + counts[4];
+ for (j = 0; j < 5; j++) {
+ if (counts[j]) {
+ len += snprintf(buf+len, sizeof buf - len - 2,
+ "%s%s: %s",
+ pre, labels[j], comma_num(counts[j]));
+ pre = ", ";
+ }
+ }
+ buf[len++] = ')';
+ }
+ buf[len] = '\0';
+ rprintf(FINFO, "%s: %s%s\n", prefix, comma_num(total), buf);
+}
+
+static void output_summary(void)
+{
+ if (INFO_GTE(STATS, 2)) {
+ rprintf(FCLIENT, "\n");
+ output_itemized_counts("Number of files", &stats.num_files);
+ if (protocol_version >= 29)
+ output_itemized_counts("Number of created files", &stats.created_files);
+ if (protocol_version >= 31)
+ output_itemized_counts("Number of deleted files", &stats.deleted_files);
+ rprintf(FINFO,"Number of regular files transferred: %s\n",
+ comma_num(stats.xferred_files));
+ rprintf(FINFO,"Total file size: %s bytes\n",
+ human_num(stats.total_size));
+ rprintf(FINFO,"Total transferred file size: %s bytes\n",
+ human_num(stats.total_transferred_size));
+ rprintf(FINFO,"Literal data: %s bytes\n",
+ human_num(stats.literal_data));
+ rprintf(FINFO,"Matched data: %s bytes\n",
+ human_num(stats.matched_data));
+ rprintf(FINFO,"File list size: %s\n",
+ human_num(stats.flist_size));
+ if (stats.flist_buildtime) {
+ rprintf(FINFO,
+ "File list generation time: %s seconds\n",
+ comma_dnum((double)stats.flist_buildtime / 1000, 3));
+ rprintf(FINFO,
+ "File list transfer time: %s seconds\n",
+ comma_dnum((double)stats.flist_xfertime / 1000, 3));
+ }
+ rprintf(FINFO,"Total bytes sent: %s\n",
+ human_num(total_written));
+ rprintf(FINFO,"Total bytes received: %s\n",
+ human_num(total_read));
+ }
+
+ if (INFO_GTE(STATS, 1)) {
+ rprintf(FCLIENT, "\n");
+ rprintf(FINFO,
+ "sent %s bytes received %s bytes %s bytes/sec\n",
+ human_num(total_written), human_num(total_read),
+ human_dnum((total_written + total_read)/(0.5 + (endtime - starttime)), 2));
+ rprintf(FINFO, "total size is %s speedup is %s%s\n",
+ human_num(stats.total_size),
+ comma_dnum((double)stats.total_size / (total_written+total_read), 2),
+ write_batch < 0 ? " (BATCH ONLY)" : dry_run ? " (DRY RUN)" : "");
+ }
+
+ fflush(stdout);
+ fflush(stderr);
+}
+
+
+/**
+ * If our C library can get malloc statistics, then show them to FINFO
+ **/
+static void show_malloc_stats(void)
+{
+#ifdef HAVE_MALLINFO
+ struct mallinfo mi;
+
+ mi = mallinfo();
+
+ rprintf(FCLIENT, "\n");
+ rprintf(FINFO, RSYNC_NAME "[%d] (%s%s%s) heap statistics:\n",
+ (int)getpid(), am_server ? "server " : "",
+ am_daemon ? "daemon " : "", who_am_i());
+ rprintf(FINFO, " arena: %10ld (bytes from sbrk)\n",
+ (long)mi.arena);
+ rprintf(FINFO, " ordblks: %10ld (chunks not in use)\n",
+ (long)mi.ordblks);
+ rprintf(FINFO, " smblks: %10ld\n",
+ (long)mi.smblks);
+ rprintf(FINFO, " hblks: %10ld (chunks from mmap)\n",
+ (long)mi.hblks);
+ rprintf(FINFO, " hblkhd: %10ld (bytes from mmap)\n",
+ (long)mi.hblkhd);
+ rprintf(FINFO, " allmem: %10ld (bytes from sbrk + mmap)\n",
+ (long)mi.arena + mi.hblkhd);
+ rprintf(FINFO, " usmblks: %10ld\n",
+ (long)mi.usmblks);
+ rprintf(FINFO, " fsmblks: %10ld\n",
+ (long)mi.fsmblks);
+ rprintf(FINFO, " uordblks: %10ld (bytes used)\n",
+ (long)mi.uordblks);
+ rprintf(FINFO, " fordblks: %10ld (bytes free)\n",
+ (long)mi.fordblks);
+ rprintf(FINFO, " keepcost: %10ld (bytes in releasable chunk)\n",
+ (long)mi.keepcost);
+#endif /* HAVE_MALLINFO */
+}
+
+
+/* Start the remote shell. cmd may be NULL to use the default. */
+static pid_t do_cmd(char *cmd, char *machine, char *user, char **remote_argv, int remote_argc,
+ int *f_in_p, int *f_out_p)
+{
+ int i, argc = 0;
+ char *args[MAX_ARGS], *need_to_free = NULL;
+ pid_t pid;
+ int dash_l_set = 0;
+
+ if (!read_batch && !local_server) {
+ char *t, *f, in_quote = '\0';
+ char *rsh_env = getenv(RSYNC_RSH_ENV);
+ if (!cmd)
+ cmd = rsh_env;
+ if (!cmd)
+ cmd = RSYNC_RSH;
+ cmd = need_to_free = strdup(cmd);
+ if (!cmd)
+ goto oom;
+
+ for (t = f = cmd; *f; f++) {
+ if (*f == ' ')
+ continue;
+ /* Comparison leaves rooms for server_options(). */
+ if (argc >= MAX_ARGS - MAX_SERVER_ARGS)
+ goto arg_overflow;
+ args[argc++] = t;
+ while (*f != ' ' || in_quote) {
+ if (!*f) {
+ if (in_quote) {
+ rprintf(FERROR,
+ "Missing trailing-%c in remote-shell command.\n",
+ in_quote);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ f--;
+ break;
+ }
+ if (*f == '\'' || *f == '"') {
+ if (!in_quote) {
+ in_quote = *f++;
+ continue;
+ }
+ if (*f == in_quote && *++f != in_quote) {
+ in_quote = '\0';
+ continue;
+ }
+ }
+ *t++ = *f++;
+ }
+ *t++ = '\0';
+ }
+
+ /* check to see if we've already been given '-l user' in
+ * the remote-shell command */
+ for (i = 0; i < argc-1; i++) {
+ if (!strcmp(args[i], "-l") && args[i+1][0] != '-')
+ dash_l_set = 1;
+ }
+
+#ifdef HAVE_REMSH
+ /* remsh (on HPUX) takes the arguments the other way around */
+ args[argc++] = machine;
+ if (user && !(daemon_over_rsh && dash_l_set)) {
+ args[argc++] = "-l";
+ args[argc++] = user;
+ }
+#else
+ if (user && !(daemon_over_rsh && dash_l_set)) {
+ args[argc++] = "-l";
+ args[argc++] = user;
+ }
+ args[argc++] = machine;
+#endif
+
+ args[argc++] = rsync_path;
+
+ if (blocking_io < 0) {
+ char *cp;
+ if ((cp = strrchr(cmd, '/')) != NULL)
+ cp++;
+ else
+ cp = cmd;
+ if (strcmp(cp, "rsh") == 0 || strcmp(cp, "remsh") == 0)
+ blocking_io = 1;
+ }
+
+ server_options(args,&argc);
+
+ if (argc >= MAX_ARGS - 2)
+ goto arg_overflow;
+ }
+
+ args[argc++] = ".";
+
+ if (!daemon_over_rsh) {
+ while (remote_argc > 0) {
+ if (argc >= MAX_ARGS - 1) {
+ arg_overflow:
+ rprintf(FERROR, "internal: args[] overflowed in do_cmd()\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (**remote_argv == '-') {
+ if (asprintf(args + argc++, "./%s", *remote_argv++) < 0)
+ out_of_memory("do_cmd");
+ } else
+ args[argc++] = *remote_argv++;
+ remote_argc--;
+ }
+ }
+
+ args[argc] = NULL;
+
+ if (DEBUG_GTE(CMD, 2)) {
+ for (i = 0; i < argc; i++)
+ rprintf(FCLIENT, "cmd[%d]=%s ", i, args[i]);
+ rprintf(FCLIENT, "\n");
+ }
+
+ if (read_batch) {
+ int from_gen_pipe[2];
+ set_allow_inc_recurse();
+ if (fd_pair(from_gen_pipe) < 0) {
+ rsyserr(FERROR, errno, "pipe");
+ exit_cleanup(RERR_IPC);
+ }
+ batch_gen_fd = from_gen_pipe[0];
+ *f_out_p = from_gen_pipe[1];
+ *f_in_p = batch_fd;
+ pid = (pid_t)-1; /* no child pid */
+#ifdef ICONV_CONST
+ setup_iconv();
+#endif
+ } else if (local_server) {
+ /* If the user didn't request --[no-]whole-file, force
+ * it on, but only if we're not batch processing. */
+ if (whole_file < 0 && !write_batch)
+ whole_file = 1;
+ set_allow_inc_recurse();
+ pid = local_child(argc, args, f_in_p, f_out_p, child_main);
+#ifdef ICONV_CONST
+ setup_iconv();
+#endif
+ } else {
+ pid = piped_child(args, f_in_p, f_out_p);
+#ifdef ICONV_CONST
+ setup_iconv();
+#endif
+ if (protect_args && !daemon_over_rsh)
+ send_protected_args(*f_out_p, args);
+ }
+
+ if (need_to_free)
+ free(need_to_free);
+
+ return pid;
+
+ oom:
+ out_of_memory("do_cmd");
+ return 0; /* not reached */
+}
+
+/* The receiving side operates in one of two modes:
+ *
+ * 1. it receives any number of files into a destination directory,
+ * placing them according to their names in the file-list.
+ *
+ * 2. it receives a single file and saves it using the name in the
+ * destination path instead of its file-list name. This requires a
+ * "local name" for writing out the destination file.
+ *
+ * So, our task is to figure out what mode/local-name we need.
+ * For mode 1, we change into the destination directory and return NULL.
+ * For mode 2, we change into the directory containing the destination
+ * file (if we aren't already there) and return the local-name. */
+static char *get_local_name(struct file_list *flist, char *dest_path)
+{
+ STRUCT_STAT st;
+ int statret;
+ char *cp;
+
+ if (DEBUG_GTE(RECV, 1)) {
+ rprintf(FINFO, "get_local_name count=%d %s\n",
+ file_total, NS(dest_path));
+ }
+
+ if (!dest_path || list_only)
+ return NULL;
+
+ /* Treat an empty string as a copy into the current directory. */
+ if (!*dest_path)
+ dest_path = ".";
+
+ if (daemon_filter_list.head) {
+ char *slash = strrchr(dest_path, '/');
+ if (slash && (slash[1] == '\0' || (slash[1] == '.' && slash[2] == '\0')))
+ *slash = '\0';
+ else
+ slash = NULL;
+ if ((*dest_path != '.' || dest_path[1] != '\0')
+ && (check_filter(&daemon_filter_list, FLOG, dest_path, 0) < 0
+ || check_filter(&daemon_filter_list, FLOG, dest_path, 1) < 0)) {
+ rprintf(FERROR, "ERROR: daemon has excluded destination \"%s\"\n",
+ dest_path);
+ exit_cleanup(RERR_FILESELECT);
+ }
+ if (slash)
+ *slash = '/';
+ }
+
+ /* See what currently exists at the destination. */
+ if ((statret = do_stat(dest_path, &st)) == 0) {
+ /* If the destination is a dir, enter it and use mode 1. */
+ if (S_ISDIR(st.st_mode)) {
+ if (!change_dir(dest_path, CD_NORMAL)) {
+ rsyserr(FERROR, errno, "change_dir#1 %s failed",
+ full_fname(dest_path));
+ exit_cleanup(RERR_FILESELECT);
+ }
+ filesystem_dev = st.st_dev; /* ensures --force works right w/-x */
+ return NULL;
+ }
+ if (file_total > 1) {
+ rprintf(FERROR,
+ "ERROR: destination must be a directory when"
+ " copying more than 1 file\n");
+ exit_cleanup(RERR_FILESELECT);
+ }
+ if (file_total == 1 && S_ISDIR(flist->files[0]->mode)) {
+ rprintf(FERROR,
+ "ERROR: cannot overwrite non-directory"
+ " with a directory\n");
+ exit_cleanup(RERR_FILESELECT);
+ }
+ } else if (errno != ENOENT) {
+ /* If we don't know what's at the destination, fail. */
+ rsyserr(FERROR, errno, "ERROR: cannot stat destination %s",
+ full_fname(dest_path));
+ exit_cleanup(RERR_FILESELECT);
+ }
+
+ cp = strrchr(dest_path, '/');
+
+ /* If we need a destination directory because the transfer is not
+ * of a single non-directory or the user has requested one via a
+ * destination path ending in a slash, create one and use mode 1. */
+ if (file_total > 1 || (cp && !cp[1])) {
+ /* Lop off the final slash (if any). */
+ if (cp && !cp[1])
+ *cp = '\0';
+
+ if (statret == 0) {
+ rprintf(FERROR,
+ "ERROR: destination path is not a directory\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+
+ if (do_mkdir(dest_path, ACCESSPERMS) != 0) {
+ rsyserr(FERROR, errno, "mkdir %s failed",
+ full_fname(dest_path));
+ exit_cleanup(RERR_FILEIO);
+ }
+
+ if (flist->high >= flist->low
+ && strcmp(flist->files[flist->low]->basename, ".") == 0)
+ flist->files[0]->flags |= FLAG_DIR_CREATED;
+
+ if (INFO_GTE(NAME, 1))
+ rprintf(FINFO, "created directory %s\n", dest_path);
+
+ if (dry_run) {
+ /* Indicate that dest dir doesn't really exist. */
+ dry_run++;
+ }
+
+ if (!change_dir(dest_path, dry_run > 1 ? CD_SKIP_CHDIR : CD_NORMAL)) {
+ rsyserr(FERROR, errno, "change_dir#2 %s failed",
+ full_fname(dest_path));
+ exit_cleanup(RERR_FILESELECT);
+ }
+
+ return NULL;
+ }
+
+ /* Otherwise, we are writing a single file, possibly on top of an
+ * existing non-directory. Change to the item's parent directory
+ * (if it has a path component), return the basename of the
+ * destination file as the local name, and use mode 2. */
+ if (!cp)
+ return dest_path;
+
+ if (cp == dest_path)
+ dest_path = "/";
+
+ *cp = '\0';
+ if (!change_dir(dest_path, CD_NORMAL)) {
+ rsyserr(FERROR, errno, "change_dir#3 %s failed",
+ full_fname(dest_path));
+ exit_cleanup(RERR_FILESELECT);
+ }
+ *cp = '/';
+
+ return cp + 1;
+}
+
+/* This function checks on our alternate-basis directories. If we're in
+ * dry-run mode and the destination dir does not yet exist, we'll try to
+ * tweak any dest-relative paths to make them work for a dry-run (the
+ * destination dir must be in curr_dir[] when this function is called).
+ * We also warn about any arg that is non-existent or not a directory. */
+static void check_alt_basis_dirs(void)
+{
+ STRUCT_STAT st;
+ char *slash = strrchr(curr_dir, '/');
+ int j;
+
+ for (j = 0; j < basis_dir_cnt; j++) {
+ char *bdir = basis_dir[j];
+ int bd_len = strlen(bdir);
+ if (bd_len > 1 && bdir[bd_len-1] == '/')
+ bdir[--bd_len] = '\0';
+ if (dry_run > 1 && *bdir != '/') {
+ int len = curr_dir_len + 1 + bd_len + 1;
+ char *new = new_array(char, len);
+ if (!new)
+ out_of_memory("check_alt_basis_dirs");
+ if (slash && strncmp(bdir, "../", 3) == 0) {
+ /* We want to remove only one leading "../" prefix for
+ * the directory we couldn't create in dry-run mode:
+ * this ensures that any other ".." references get
+ * evaluated the same as they would for a live copy. */
+ *slash = '\0';
+ pathjoin(new, len, curr_dir, bdir + 3);
+ *slash = '/';
+ } else
+ pathjoin(new, len, curr_dir, bdir);
+ basis_dir[j] = bdir = new;
+ }
+ if (do_stat(bdir, &st) < 0)
+ rprintf(FWARNING, "%s arg does not exist: %s\n", dest_option, bdir);
+ else if (!S_ISDIR(st.st_mode))
+ rprintf(FWARNING, "%s arg is not a dir: %s\n", dest_option, bdir);
+ }
+}
+
+/* This is only called by the sender. */
+static void read_final_goodbye(int f_in, int f_out)
+{
+ int i, iflags, xlen;
+ uchar fnamecmp_type;
+ char xname[MAXPATHLEN];
+
+ shutting_down = True;
+
+ if (protocol_version < 29)
+ i = read_int(f_in);
+ else {
+ i = read_ndx_and_attrs(f_in, f_out, &iflags, &fnamecmp_type, xname, &xlen);
+ if (protocol_version >= 31 && i == NDX_DONE) {
+ if (am_sender)
+ write_ndx(f_out, NDX_DONE);
+ else {
+ if (batch_gen_fd >= 0) {
+ while (read_int(batch_gen_fd) != NDX_DEL_STATS) {}
+ read_del_stats(batch_gen_fd);
+ }
+ write_int(f_out, NDX_DONE);
+ }
+ i = read_ndx_and_attrs(f_in, f_out, &iflags, &fnamecmp_type, xname, &xlen);
+ }
+ }
+
+ if (i != NDX_DONE) {
+ rprintf(FERROR, "Invalid packet at end of run (%d) [%s]\n",
+ i, who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
+}
+
+static void do_server_sender(int f_in, int f_out, int argc, char *argv[])
+{
+ struct file_list *flist;
+ char *dir = argv[0];
+
+ if (DEBUG_GTE(SEND, 1))
+ rprintf(FINFO, "server_sender starting pid=%d\n", (int)getpid());
+
+ if (am_daemon && lp_write_only(module_id)) {
+ rprintf(FERROR, "ERROR: module is write only\n");
+ exit_cleanup(RERR_SYNTAX);
+ return;
+ }
+ if (am_daemon && read_only && remove_source_files) {
+ rprintf(FERROR,
+ "ERROR: --remove-%s-files cannot be used with a read-only module\n",
+ remove_source_files == 1 ? "source" : "sent");
+ exit_cleanup(RERR_SYNTAX);
+ return;
+ }
+
+ if (!relative_paths) {
+ if (!change_dir(dir, CD_NORMAL)) {
+ rsyserr(FERROR, errno, "change_dir#3 %s failed",
+ full_fname(dir));
+ exit_cleanup(RERR_FILESELECT);
+ }
+ }
+ argc--;
+ argv++;
+
+ if (argc == 0 && (recurse || xfer_dirs || list_only)) {
+ argc = 1;
+ argv--;
+ argv[0] = ".";
+ }
+
+ flist = send_file_list(f_out,argc,argv);
+ if (!flist || flist->used == 0) {
+ /* Make sure input buffering is off so we can't hang in noop_io_until_death(). */
+ io_end_buffering_in(0);
+ /* TODO: we should really exit in a more controlled manner. */
+ exit_cleanup(0);
+ }
+
+ io_start_buffering_in(f_in);
+
+ send_files(f_in, f_out);
+ io_flush(FULL_FLUSH);
+ handle_stats(f_out);
+ if (protocol_version >= 24)
+ read_final_goodbye(f_in, f_out);
+ io_flush(FULL_FLUSH);
+ exit_cleanup(0);
+}
+
+
+static int do_recv(int f_in, int f_out, char *local_name)
+{
+ int pid;
+ int exit_code = 0;
+ int error_pipe[2];
+
+ /* The receiving side mustn't obey this, or an existing symlink that
+ * points to an identical file won't be replaced by the referent. */
+ copy_links = copy_dirlinks = copy_unsafe_links = 0;
+
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && !inc_recurse)
+ match_hard_links(first_flist);
+#endif
+
+ if (fd_pair(error_pipe) < 0) {
+ rsyserr(FERROR, errno, "pipe failed in do_recv");
+ exit_cleanup(RERR_IPC);
+ }
+
+ if (backup_dir) {
+ int ret = make_path(backup_dir_buf, MKP_DROP_NAME); /* drops trailing slash */
+ if (ret < 0)
+ exit_cleanup(RERR_SYNTAX);
+ if (ret)
+ rprintf(FINFO, "Created backup_dir %s\n", backup_dir_buf);
+ else if (INFO_GTE(BACKUP, 1))
+ rprintf(FINFO, "backup_dir is %s\n", backup_dir_buf);
+ }
+
+ io_flush(FULL_FLUSH);
+
+ if ((pid = do_fork()) == -1) {
+ rsyserr(FERROR, errno, "fork failed in do_recv");
+ exit_cleanup(RERR_IPC);
+ }
+
+ if (pid == 0) {
+ am_receiver = 1;
+ send_msgs_to_gen = am_server;
+
+ close(error_pipe[0]);
+
+ /* We can't let two processes write to the socket at one time. */
+ io_end_multiplex_out(MPLX_SWITCHING);
+ if (f_in != f_out)
+ close(f_out);
+ sock_f_out = -1;
+ f_out = error_pipe[1];
+
+ bwlimit_writemax = 0; /* receiver doesn't need to do this */
+
+ if (read_batch)
+ io_start_buffering_in(f_in);
+ io_start_multiplex_out(f_out);
+
+ recv_files(f_in, f_out, local_name);
+ io_flush(FULL_FLUSH);
+ handle_stats(f_in);
+
+ if (output_needs_newline) {
+ fputc('\n', stdout);
+ output_needs_newline = 0;
+ }
+
+ write_int(f_out, NDX_DONE);
+ send_msg(MSG_STATS, (char*)&stats.total_read, sizeof stats.total_read, 0);
+ io_flush(FULL_FLUSH);
+
+ /* Handle any keep-alive packets from the post-processing work
+ * that the generator does. */
+ if (protocol_version >= 29) {
+ kluge_around_eof = -1;
+
+ /* This should only get stopped via a USR2 signal. */
+ read_final_goodbye(f_in, f_out);
+
+ rprintf(FERROR, "Invalid packet at end of run [%s]\n",
+ who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ /* Finally, we go to sleep until our parent kills us with a
+ * USR2 signal. We sleep for a short time, as on some OSes
+ * a signal won't interrupt a sleep! */
+ while (1)
+ msleep(20);
+ }
+
+ am_generator = 1;
+ flist_receiving_enabled = True;
+
+ io_end_multiplex_in(MPLX_SWITCHING);
+ if (write_batch && !am_server)
+ stop_write_batch();
+
+ close(error_pipe[1]);
+ if (f_in != f_out)
+ close(f_in);
+ sock_f_in = -1;
+ f_in = error_pipe[0];
+
+ io_start_buffering_out(f_out);
+ io_start_multiplex_in(f_in);
+
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && inc_recurse) {
+ struct file_list *flist;
+ for (flist = first_flist; flist; flist = flist->next)
+ match_hard_links(flist);
+ }
+#endif
+
+ generate_files(f_out, local_name);
+
+ handle_stats(-1);
+ io_flush(FULL_FLUSH);
+ shutting_down = True;
+ if (protocol_version >= 24) {
+ /* send a final goodbye message */
+ write_ndx(f_out, NDX_DONE);
+ }
+ io_flush(FULL_FLUSH);
+
+ kill(pid, SIGUSR2);
+ wait_process_with_flush(pid, &exit_code);
+ return exit_code;
+}
+
+static void do_server_recv(int f_in, int f_out, int argc, char *argv[])
+{
+ int exit_code;
+ struct file_list *flist;
+ char *local_name = NULL;
+ int negated_levels;
+
+ if (filesfrom_fd >= 0 && !msgs2stderr && protocol_version < 31) {
+ /* We can't mix messages with files-from data on the socket,
+ * so temporarily turn off info/debug messages. */
+ negate_output_levels();
+ negated_levels = 1;
+ } else
+ negated_levels = 0;
+
+ if (DEBUG_GTE(RECV, 1))
+ rprintf(FINFO, "server_recv(%d) starting pid=%d\n", argc, (int)getpid());
+
+ if (am_daemon && read_only) {
+ rprintf(FERROR,"ERROR: module is read only\n");
+ exit_cleanup(RERR_SYNTAX);
+ return;
+ }
+
+ if (argc > 0) {
+ char *dir = argv[0];
+ argc--;
+ argv++;
+ if (!am_daemon && !change_dir(dir, CD_NORMAL)) {
+ rsyserr(FERROR, errno, "change_dir#4 %s failed",
+ full_fname(dir));
+ exit_cleanup(RERR_FILESELECT);
+ }
+ }
+
+ if (protocol_version >= 30)
+ io_start_multiplex_in(f_in);
+ else
+ io_start_buffering_in(f_in);
+ recv_filter_list(f_in);
+
+ if (filesfrom_fd >= 0) {
+ /* We need to send the files-from names to the sender at the
+ * same time that we receive the file-list from them, so we
+ * need the IO routines to automatically write out the names
+ * onto our f_out socket as we read the file-list. This
+ * avoids both deadlock and extra delays/buffers. */
+ start_filesfrom_forwarding(filesfrom_fd);
+ filesfrom_fd = -1;
+ }
+
+ flist = recv_file_list(f_in);
+ if (!flist) {
+ rprintf(FERROR,"server_recv: recv_file_list error\n");
+ exit_cleanup(RERR_FILESELECT);
+ }
+ if (inc_recurse && file_total == 1)
+ recv_additional_file_list(f_in);
+
+ if (negated_levels)
+ negate_output_levels();
+
+ if (argc > 0)
+ local_name = get_local_name(flist,argv[0]);
+
+ /* Now that we know what our destination directory turned out to be,
+ * we can sanitize the --link-/copy-/compare-dest args correctly. */
+ if (sanitize_paths) {
+ char **dir_p;
+ for (dir_p = basis_dir; *dir_p; dir_p++)
+ *dir_p = sanitize_path(NULL, *dir_p, NULL, curr_dir_depth, SP_DEFAULT);
+ if (partial_dir)
+ partial_dir = sanitize_path(NULL, partial_dir, NULL, curr_dir_depth, SP_DEFAULT);
+ }
+ check_alt_basis_dirs();
+
+ if (daemon_filter_list.head) {
+ char **dir_p;
+ filter_rule_list *elp = &daemon_filter_list;
+
+ for (dir_p = basis_dir; *dir_p; dir_p++) {
+ char *dir = *dir_p;
+ if (*dir == '/')
+ dir += module_dirlen;
+ if (check_filter(elp, FLOG, dir, 1) < 0)
+ goto options_rejected;
+ }
+ if (partial_dir && *partial_dir == '/'
+ && check_filter(elp, FLOG, partial_dir + module_dirlen, 1) < 0) {
+ options_rejected:
+ rprintf(FERROR,
+ "Your options have been rejected by the server.\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ }
+
+ exit_code = do_recv(f_in, f_out, local_name);
+ exit_cleanup(exit_code);
+}
+
+
+int child_main(int argc, char *argv[])
+{
+ start_server(STDIN_FILENO, STDOUT_FILENO, argc, argv);
+ return 0;
+}
+
+
+void start_server(int f_in, int f_out, int argc, char *argv[])
+{
+ set_nonblocking(f_in);
+ set_nonblocking(f_out);
+
+ io_set_sock_fds(f_in, f_out);
+ setup_protocol(f_out, f_in);
+
+ if (protocol_version >= 23)
+ io_start_multiplex_out(f_out);
+ if (am_daemon && io_timeout && protocol_version >= 31)
+ send_msg_int(MSG_IO_TIMEOUT, io_timeout);
+
+ if (am_sender) {
+ keep_dirlinks = 0; /* Must be disabled on the sender. */
+ if (need_messages_from_generator)
+ io_start_multiplex_in(f_in);
+ else
+ io_start_buffering_in(f_in);
+ recv_filter_list(f_in);
+ do_server_sender(f_in, f_out, argc, argv);
+ } else
+ do_server_recv(f_in, f_out, argc, argv);
+ exit_cleanup(0);
+}
+
+/* This is called once the connection has been negotiated. It is used
+ * for rsyncd, remote-shell, and local connections. */
+int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[])
+{
+ struct file_list *flist = NULL;
+ int exit_code = 0, exit_code2 = 0;
+ char *local_name = NULL;
+
+ cleanup_child_pid = pid;
+ if (!read_batch) {
+ set_nonblocking(f_in);
+ set_nonblocking(f_out);
+ }
+
+ io_set_sock_fds(f_in, f_out);
+ setup_protocol(f_out,f_in);
+
+ /* We set our stderr file handle to blocking because ssh might have
+ * set it to non-blocking. This can be particularly troublesome if
+ * stderr is a clone of stdout, because ssh would have set our stdout
+ * to non-blocking at the same time (which can easily cause us to lose
+ * output from our print statements). This kluge shouldn't cause ssh
+ * any problems for how we use it. Note also that we delayed setting
+ * this until after the above protocol setup so that we know for sure
+ * that ssh is done twiddling its file descriptors. */
+ set_blocking(STDERR_FILENO);
+
+ if (am_sender) {
+ keep_dirlinks = 0; /* Must be disabled on the sender. */
+
+ if (always_checksum
+ && (log_format_has(stdout_format, 'C')
+ || log_format_has(logfile_format, 'C')))
+ sender_keeps_checksum = 1;
+
+ if (protocol_version >= 30)
+ io_start_multiplex_out(f_out);
+ else
+ io_start_buffering_out(f_out);
+ if (protocol_version >= 31 || (!filesfrom_host && protocol_version >= 23))
+ io_start_multiplex_in(f_in);
+ else
+ io_start_buffering_in(f_in);
+ send_filter_list(f_out);
+ if (filesfrom_host)
+ filesfrom_fd = f_in;
+
+ if (write_batch && !am_server)
+ start_write_batch(f_out);
+ flist = send_file_list(f_out, argc, argv);
+ if (DEBUG_GTE(FLIST, 3))
+ rprintf(FINFO,"file list sent\n");
+
+ if (protocol_version < 31 && filesfrom_host && protocol_version >= 23)
+ io_start_multiplex_in(f_in);
+
+ io_flush(NORMAL_FLUSH);
+ send_files(f_in, f_out);
+ io_flush(FULL_FLUSH);
+ handle_stats(-1);
+ if (protocol_version >= 24)
+ read_final_goodbye(f_in, f_out);
+ if (pid != -1) {
+ if (DEBUG_GTE(EXIT, 2))
+ rprintf(FINFO,"client_run waiting on %d\n", (int) pid);
+ io_flush(FULL_FLUSH);
+ wait_process_with_flush(pid, &exit_code);
+ }
+ output_summary();
+ io_flush(FULL_FLUSH);
+ exit_cleanup(exit_code);
+ }
+
+ if (!read_batch) {
+ if (protocol_version >= 23)
+ io_start_multiplex_in(f_in);
+ if (need_messages_from_generator)
+ io_start_multiplex_out(f_out);
+ else
+ io_start_buffering_out(f_out);
+ }
+
+ send_filter_list(read_batch ? -1 : f_out);
+
+ if (filesfrom_fd >= 0) {
+ start_filesfrom_forwarding(filesfrom_fd);
+ filesfrom_fd = -1;
+ }
+
+ if (write_batch && !am_server)
+ start_write_batch(f_in);
+ flist = recv_file_list(f_in);
+ if (inc_recurse && file_total == 1)
+ recv_additional_file_list(f_in);
+
+ if (flist && flist->used > 0) {
+ local_name = get_local_name(flist, argv[0]);
+
+ check_alt_basis_dirs();
+
+ exit_code2 = do_recv(f_in, f_out, local_name);
+ } else {
+ handle_stats(-1);
+ output_summary();
+ }
+
+ if (pid != -1) {
+ if (DEBUG_GTE(RECV, 1))
+ rprintf(FINFO,"client_run2 waiting on %d\n", (int) pid);
+ io_flush(FULL_FLUSH);
+ wait_process_with_flush(pid, &exit_code);
+ }
+
+ return MAX(exit_code, exit_code2);
+}
+
+static int copy_argv(char *argv[])
+{
+ int i;
+
+ for (i = 0; argv[i]; i++) {
+ if (!(argv[i] = strdup(argv[i]))) {
+ rprintf (FERROR, "out of memory at %s(%d)\n",
+ __FILE__, __LINE__);
+ return RERR_MALLOC;
+ }
+ }
+
+ return 0;
+}
+
+
+/* Start a client for either type of remote connection. Work out
+ * whether the arguments request a remote shell or rsyncd connection,
+ * and call the appropriate connection function, then run_client.
+ *
+ * Calls either start_socket_client (for sockets) or do_cmd and
+ * client_run (for ssh). */
+static int start_client(int argc, char *argv[])
+{
+ char *p, *shell_machine = NULL, *shell_user = NULL;
+ char **remote_argv;
+ int remote_argc;
+ int f_in, f_out;
+ int ret;
+ pid_t pid;
+
+ /* Don't clobber argv[] so that ps(1) can still show the right
+ * command line. */
+ if ((ret = copy_argv(argv)) != 0)
+ return ret;
+
+ if (!read_batch) { /* for read_batch, NO source is specified */
+ char *path = check_for_hostspec(argv[0], &shell_machine, &rsync_port);
+ if (path) { /* source is remote */
+ char *dummy_host;
+ int dummy_port = 0;
+ *argv = path;
+ remote_argv = argv;
+ remote_argc = argc;
+ argv += argc - 1;
+ if (argc == 1 || **argv == ':')
+ argc = 0; /* no dest arg */
+ else if (check_for_hostspec(*argv, &dummy_host, &dummy_port)) {
+ rprintf(FERROR,
+ "The source and destination cannot both be remote.\n");
+ exit_cleanup(RERR_SYNTAX);
+ } else {
+ remote_argc--; /* don't count dest */
+ argc = 1;
+ }
+ if (filesfrom_host && *filesfrom_host
+ && strcmp(filesfrom_host, shell_machine) != 0) {
+ rprintf(FERROR,
+ "--files-from hostname is not the same as the transfer hostname\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ am_sender = 0;
+ if (rsync_port)
+ daemon_over_rsh = shell_cmd ? 1 : -1;
+ } else { /* source is local, check dest arg */
+ am_sender = 1;
+
+ if (argc > 1) {
+ p = argv[--argc];
+ remote_argv = argv + argc;
+ } else {
+ static char *dotarg[1] = { "." };
+ p = dotarg[0];
+ remote_argv = dotarg;
+ }
+ remote_argc = 1;
+
+ path = check_for_hostspec(p, &shell_machine, &rsync_port);
+ if (path && filesfrom_host && *filesfrom_host
+ && strcmp(filesfrom_host, shell_machine) != 0) {
+ rprintf(FERROR,
+ "--files-from hostname is not the same as the transfer hostname\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (!path) { /* no hostspec found, so src & dest are local */
+ local_server = 1;
+ if (filesfrom_host) {
+ rprintf(FERROR,
+ "--files-from cannot be remote when the transfer is local\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ shell_machine = NULL;
+ } else { /* hostspec was found, so dest is remote */
+ argv[argc] = path;
+ if (rsync_port)
+ daemon_over_rsh = shell_cmd ? 1 : -1;
+ }
+ }
+ } else { /* read_batch */
+ local_server = 1;
+ if (check_for_hostspec(argv[argc-1], &shell_machine, &rsync_port)) {
+ rprintf(FERROR, "remote destination is not allowed with --read-batch\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ remote_argv = argv += argc - 1;
+ remote_argc = argc = 1;
+ }
+
+ if (!rsync_port && remote_argc && !**remote_argv) /* Turn an empty arg into a dot dir. */
+ *remote_argv = ".";
+
+ if (am_sender) {
+ char *dummy_host;
+ int dummy_port = rsync_port;
+ int i;
+ /* For local source, extra source args must not have hostspec. */
+ for (i = 1; i < argc; i++) {
+ if (check_for_hostspec(argv[i], &dummy_host, &dummy_port)) {
+ rprintf(FERROR, "Unexpected remote arg: %s\n", argv[i]);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ }
+ } else {
+ char *dummy_host;
+ int dummy_port = rsync_port;
+ int i;
+ /* For remote source, any extra source args must have either
+ * the same hostname or an empty hostname. */
+ for (i = 1; i < remote_argc; i++) {
+ char *arg = check_for_hostspec(remote_argv[i], &dummy_host, &dummy_port);
+ if (!arg) {
+ rprintf(FERROR, "Unexpected local arg: %s\n", remote_argv[i]);
+ rprintf(FERROR, "If arg is a remote file/dir, prefix it with a colon (:).\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (*dummy_host && strcmp(dummy_host, shell_machine) != 0) {
+ rprintf(FERROR, "All source args must come from the same machine.\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (rsync_port != dummy_port) {
+ if (!rsync_port || !dummy_port)
+ rprintf(FERROR, "All source args must use the same hostspec format.\n");
+ else
+ rprintf(FERROR, "All source args must use the same port number.\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (!rsync_port && !*arg) /* Turn an empty arg into a dot dir. */
+ arg = ".";
+ remote_argv[i] = arg;
+ }
+ }
+
+ if (daemon_over_rsh < 0)
+ return start_socket_client(shell_machine, remote_argc, remote_argv, argc, argv);
+
+ if (password_file && !daemon_over_rsh) {
+ rprintf(FERROR, "The --password-file option may only be "
+ "used when accessing an rsync daemon.\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+
+ if (connect_timeout) {
+ rprintf(FERROR, "The --contimeout option may only be "
+ "used when connecting to an rsync daemon.\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+
+ if (shell_machine) {
+ p = strrchr(shell_machine,'@');
+ if (p) {
+ *p = 0;
+ shell_user = shell_machine;
+ shell_machine = p+1;
+ }
+ }
+
+ if (DEBUG_GTE(CMD, 2)) {
+ rprintf(FINFO,"cmd=%s machine=%s user=%s path=%s\n",
+ NS(shell_cmd), NS(shell_machine), NS(shell_user),
+ NS(remote_argv[0]));
+ }
+
+ pid = do_cmd(shell_cmd, shell_machine, shell_user, remote_argv, remote_argc,
+ &f_in, &f_out);
+
+ /* if we're running an rsync server on the remote host over a
+ * remote shell command, we need to do the RSYNCD protocol first */
+ if (daemon_over_rsh) {
+ int tmpret;
+ tmpret = start_inband_exchange(f_in, f_out, shell_user, remote_argc, remote_argv);
+ if (tmpret < 0)
+ return tmpret;
+ }
+
+ ret = client_run(f_in, f_out, pid, argc, argv);
+
+ fflush(stdout);
+ fflush(stderr);
+
+ return ret;
+}
+
+
+static RETSIGTYPE sigusr1_handler(UNUSED(int val))
+{
+ exit_cleanup(RERR_SIGNAL1);
+}
+
+static RETSIGTYPE sigusr2_handler(UNUSED(int val))
+{
+ if (!am_server)
+ output_summary();
+ close_all();
+ if (got_xfer_error)
+ _exit(RERR_PARTIAL);
+ _exit(0);
+}
+
+RETSIGTYPE remember_children(UNUSED(int val))
+{
+#ifdef WNOHANG
+ int cnt, status;
+ pid_t pid;
+ /* An empty waitpid() loop was put here by Tridge and we could never
+ * get him to explain why he put it in, so rather than taking it
+ * out we're instead saving the child exit statuses for later use.
+ * The waitpid() loop presumably eliminates all possibility of leaving
+ * zombie children, maybe that's why he did it. */
+ while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
+ /* save the child's exit status */
+ for (cnt = 0; cnt < MAXCHILDPROCS; cnt++) {
+ if (pid_stat_table[cnt].pid == 0) {
+ pid_stat_table[cnt].pid = pid;
+ pid_stat_table[cnt].status = status;
+ break;
+ }
+ }
+ }
+#endif
+#ifndef HAVE_SIGACTION
+ signal(SIGCHLD, remember_children);
+#endif
+}
+
+
+/**
+ * This routine catches signals and tries to send them to gdb.
+ *
+ * Because it's called from inside a signal handler it ought not to
+ * use too many library routines.
+ *
+ * @todo Perhaps use "screen -X" instead/as well, to help people
+ * debugging without easy access to X. Perhaps use an environment
+ * variable, or just call a script?
+ *
+ * @todo The /proc/ magic probably only works on Linux (and
+ * Solaris?) Can we be more portable?
+ **/
+#ifdef MAINTAINER_MODE
+const char *get_panic_action(void)
+{
+ const char *cmd_fmt = getenv("RSYNC_PANIC_ACTION");
+
+ if (cmd_fmt)
+ return cmd_fmt;
+ else
+ return "xterm -display :0 -T Panic -n Panic "
+ "-e gdb /proc/%d/exe %d";
+}
+
+
+/**
+ * Handle a fatal signal by launching a debugger, controlled by $RSYNC_PANIC_ACTION.
+ *
+ * This signal handler is only installed if we were configured with
+ * --enable-maintainer-mode. Perhaps it should always be on and we
+ * should just look at the environment variable, but I'm a bit leery
+ * of a signal sending us into a busy loop.
+ **/
+static RETSIGTYPE rsync_panic_handler(UNUSED(int whatsig))
+{
+ char cmd_buf[300];
+ int ret, pid_int = getpid();
+
+ snprintf(cmd_buf, sizeof cmd_buf, get_panic_action(), pid_int, pid_int);
+
+ /* Unless we failed to execute gdb, we allow the process to
+ * continue. I'm not sure if that's right. */
+ ret = system(cmd_buf);
+ if (ret)
+ _exit(ret);
+}
+#endif
+
+
+int main(int argc,char *argv[])
+{
+ int ret;
+ int orig_argc = argc;
+ char **orig_argv = argv;
+#ifdef HAVE_SIGACTION
+# ifdef HAVE_SIGPROCMASK
+ sigset_t sigmask;
+
+ sigemptyset(&sigmask);
+# endif
+ sigact.sa_flags = SA_NOCLDSTOP;
+#endif
+ SIGACTMASK(SIGUSR1, sigusr1_handler);
+ SIGACTMASK(SIGUSR2, sigusr2_handler);
+ SIGACTMASK(SIGCHLD, remember_children);
+#ifdef MAINTAINER_MODE
+ SIGACTMASK(SIGSEGV, rsync_panic_handler);
+ SIGACTMASK(SIGFPE, rsync_panic_handler);
+ SIGACTMASK(SIGABRT, rsync_panic_handler);
+ SIGACTMASK(SIGBUS, rsync_panic_handler);
+#endif
+
+ starttime = time(NULL);
+ our_uid = MY_UID();
+ our_gid = MY_GID();
+ am_root = our_uid == 0;
+
+ memset(&stats, 0, sizeof(stats));
+
+ if (argc < 2) {
+ usage(FERROR);
+ exit_cleanup(RERR_SYNTAX);
+ }
+
+ /* Get the umask for use in permission calculations. We no longer set
+ * it to zero; that is ugly and pointless now that all the callers that
+ * relied on it have been reeducated to work with default ACLs. */
+ umask(orig_umask = umask(0));
+
+#if defined CONFIG_LOCALE && defined HAVE_SETLOCALE
+ setlocale(LC_CTYPE, "");
+#endif
+
+ if (!parse_arguments(&argc, (const char ***) &argv)) {
+ /* FIXME: We ought to call the same error-handling
+ * code here, rather than relying on getopt. */
+ option_error();
+ exit_cleanup(RERR_SYNTAX);
+ }
+
+ SIGACTMASK(SIGINT, sig_int);
+ SIGACTMASK(SIGHUP, sig_int);
+ SIGACTMASK(SIGTERM, sig_int);
+#if defined HAVE_SIGACTION && HAVE_SIGPROCMASK
+ sigprocmask(SIG_UNBLOCK, &sigmask, NULL);
+#endif
+
+ /* Ignore SIGPIPE; we consistently check error codes and will
+ * see the EPIPE. */
+ SIGACTION(SIGPIPE, SIG_IGN);
+#ifdef SIGXFSZ
+ SIGACTION(SIGXFSZ, SIG_IGN);
+#endif
+
+ /* Initialize change_dir() here because on some old systems getcwd
+ * (implemented by forking "pwd" and reading its output) doesn't
+ * work when there are other child processes. Also, on all systems
+ * that implement getcwd that way "pwd" can't be found after chroot. */
+ change_dir(NULL, CD_NORMAL);
+
+ init_flist();
+
+ if ((write_batch || read_batch) && !am_server) {
+ if (write_batch)
+ write_batch_shell_file(orig_argc, orig_argv, argc);
+
+ if (read_batch && strcmp(batch_name, "-") == 0)
+ batch_fd = STDIN_FILENO;
+ else {
+ batch_fd = do_open(batch_name,
+ write_batch ? O_WRONLY | O_CREAT | O_TRUNC
+ : O_RDONLY, S_IRUSR | S_IWUSR);
+ }
+ if (batch_fd < 0) {
+ rsyserr(FERROR, errno, "Batch file %s open error",
+ full_fname(batch_name));
+ exit_cleanup(RERR_FILEIO);
+ }
+ if (read_batch)
+ read_stream_flags(batch_fd);
+ else
+ write_stream_flags(batch_fd);
+ }
+ if (write_batch < 0)
+ dry_run = 1;
+
+ if (am_server) {
+#ifdef ICONV_CONST
+ setup_iconv();
+#endif
+ } else if (am_daemon)
+ return daemon_main();
+
+ if (am_server && protect_args) {
+ char buf[MAXPATHLEN];
+ protect_args = 2;
+ read_args(STDIN_FILENO, NULL, buf, sizeof buf, 1, &argv, &argc, NULL);
+ if (!parse_arguments(&argc, (const char ***) &argv)) {
+ option_error();
+ exit_cleanup(RERR_SYNTAX);
+ }
+ }
+
+ if (argc < 1) {
+ usage(FERROR);
+ exit_cleanup(RERR_SYNTAX);
+ }
+
+ if (am_server) {
+ set_nonblocking(STDIN_FILENO);
+ set_nonblocking(STDOUT_FILENO);
+ if (am_daemon)
+ return start_daemon(STDIN_FILENO, STDOUT_FILENO);
+ start_server(STDIN_FILENO, STDOUT_FILENO, argc, argv);
+ }
+
+ ret = start_client(argc, argv);
+ if (ret == -1)
+ exit_cleanup(RERR_STARTCLIENT);
+ else
+ exit_cleanup(ret);
+
+ return ret;
+}
diff --git a/rsync/match.c b/rsync/match.c
new file mode 100644
index 0000000..39d15ed
--- /dev/null
+++ b/rsync/match.c
@@ -0,0 +1,448 @@
+/*
+ * Block matching used by the file-transfer code.
+ *
+ * Copyright (C) 1996 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2003-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "inums.h"
+
+extern int checksum_seed;
+extern int append_mode;
+extern int checksum_len;
+
+int updating_basis_file;
+char sender_file_sum[MAX_DIGEST_LEN];
+
+static int false_alarms;
+static int hash_hits;
+static int matches;
+static int64 data_transfer;
+
+static int total_false_alarms;
+static int total_hash_hits;
+static int total_matches;
+
+extern struct stats stats;
+
+#define TRADITIONAL_TABLESIZE (1<<16)
+
+static uint32 tablesize;
+static int32 *hash_table;
+
+#define SUM2HASH2(s1,s2) (((s1) + (s2)) & 0xFFFF)
+#define SUM2HASH(sum) SUM2HASH2((sum)&0xFFFF,(sum)>>16)
+
+#define BIG_SUM2HASH(sum) ((sum)%tablesize)
+
+static void build_hash_table(struct sum_struct *s)
+{
+ static uint32 alloc_size;
+ int32 i;
+
+ /* Dynamically calculate the hash table size so that the hash load
+ * for big files is about 80%. A number greater than the traditional
+ * size must be odd or s2 will not be able to span the entire set. */
+ tablesize = (uint32)(s->count/8) * 10 + 11;
+ if (tablesize < TRADITIONAL_TABLESIZE)
+ tablesize = TRADITIONAL_TABLESIZE;
+ if (tablesize > alloc_size || tablesize < alloc_size - 16*1024) {
+ if (hash_table)
+ free(hash_table);
+ hash_table = new_array(int32, tablesize);
+ if (!hash_table)
+ out_of_memory("build_hash_table");
+ alloc_size = tablesize;
+ }
+
+ memset(hash_table, 0xFF, tablesize * sizeof hash_table[0]);
+
+ if (tablesize == TRADITIONAL_TABLESIZE) {
+ for (i = 0; i < s->count; i++) {
+ uint32 t = SUM2HASH(s->sums[i].sum1);
+ s->sums[i].chain = hash_table[t];
+ hash_table[t] = i;
+ }
+ } else {
+ for (i = 0; i < s->count; i++) {
+ uint32 t = BIG_SUM2HASH(s->sums[i].sum1);
+ s->sums[i].chain = hash_table[t];
+ hash_table[t] = i;
+ }
+ }
+}
+
+
+static OFF_T last_match;
+
+
+/* Transmit a literal and/or match token.
+ *
+ * This delightfully-named function is called either when we find a
+ * match and need to transmit all the unmatched data leading up to it,
+ * or when we get bored of accumulating literal data and just need to
+ * transmit it. As a result of this second case, it is called even if
+ * we have not matched at all!
+ *
+ * If i >= 0, the number of a matched token. If < 0, indicates we have
+ * only literal data. A -1 will send a 0-token-int too, and a -2 sends
+ * only literal data, w/o any token-int. */
+static void matched(int f, struct sum_struct *s, struct map_struct *buf,
+ OFF_T offset, int32 i)
+{
+ int32 n = (int32)(offset - last_match); /* max value: block_size (int32) */
+ int32 j;
+
+ if (DEBUG_GTE(DELTASUM, 2) && i >= 0) {
+ rprintf(FINFO,
+ "match at %s last_match=%s j=%d len=%ld n=%ld\n",
+ big_num(offset), big_num(last_match), i,
+ (long)s->sums[i].len, (long)n);
+ }
+
+ send_token(f, i, buf, last_match, n, i < 0 ? 0 : s->sums[i].len);
+ data_transfer += n;
+
+ if (i >= 0) {
+ stats.matched_data += s->sums[i].len;
+ n += s->sums[i].len;
+ }
+
+ for (j = 0; j < n; j += CHUNK_SIZE) {
+ int32 n1 = MIN(CHUNK_SIZE, n - j);
+ sum_update(map_ptr(buf, last_match + j, n1), n1);
+ }
+
+ if (i >= 0)
+ last_match = offset + s->sums[i].len;
+ else
+ last_match = offset;
+
+ if (buf && INFO_GTE(PROGRESS, 1))
+ show_progress(last_match, buf->file_size);
+}
+
+
+static void hash_search(int f,struct sum_struct *s,
+ struct map_struct *buf, OFF_T len)
+{
+ OFF_T offset, aligned_offset, end;
+ int32 k, want_i, aligned_i, backup;
+ char sum2[SUM_LENGTH];
+ uint32 s1, s2, sum;
+ int more;
+ schar *map;
+
+ /* want_i is used to encourage adjacent matches, allowing the RLL
+ * coding of the output to work more efficiently. */
+ want_i = 0;
+
+ if (DEBUG_GTE(DELTASUM, 2)) {
+ rprintf(FINFO, "hash search b=%ld len=%s\n",
+ (long)s->blength, big_num(len));
+ }
+
+ k = (int32)MIN(len, (OFF_T)s->blength);
+
+ map = (schar *)map_ptr(buf, 0, k);
+
+ sum = get_checksum1((char *)map, k);
+ s1 = sum & 0xFFFF;
+ s2 = sum >> 16;
+ if (DEBUG_GTE(DELTASUM, 3))
+ rprintf(FINFO, "sum=%.8x k=%ld\n", sum, (long)k);
+
+ offset = aligned_offset = aligned_i = 0;
+
+ end = len + 1 - s->sums[s->count-1].len;
+
+ if (DEBUG_GTE(DELTASUM, 3)) {
+ rprintf(FINFO, "hash search s->blength=%ld len=%s count=%s\n",
+ (long)s->blength, big_num(len), big_num(s->count));
+ }
+
+ do {
+ int done_csum2 = 0;
+ uint32 hash_entry;
+ int32 i, *prev;
+
+ if (DEBUG_GTE(DELTASUM, 4)) {
+ rprintf(FINFO, "offset=%s sum=%04x%04x\n",
+ big_num(offset), s2 & 0xFFFF, s1 & 0xFFFF);
+ }
+
+ if (tablesize == TRADITIONAL_TABLESIZE) {
+ hash_entry = SUM2HASH2(s1,s2);
+ if ((i = hash_table[hash_entry]) < 0)
+ goto null_hash;
+ sum = (s1 & 0xffff) | (s2 << 16);
+ } else {
+ sum = (s1 & 0xffff) | (s2 << 16);
+ hash_entry = BIG_SUM2HASH(sum);
+ if ((i = hash_table[hash_entry]) < 0)
+ goto null_hash;
+ }
+ prev = &hash_table[hash_entry];
+
+ hash_hits++;
+ do {
+ int32 l;
+
+ /* When updating in-place, the chunk's offset must be
+ * either >= our offset or identical data at that offset.
+ * Remove any bypassed entries that we can never use. */
+ if (updating_basis_file && s->sums[i].offset < offset
+ && !(s->sums[i].flags & SUMFLG_SAME_OFFSET)) {
+ *prev = s->sums[i].chain;
+ continue;
+ }
+ prev = &s->sums[i].chain;
+
+ if (sum != s->sums[i].sum1)
+ continue;
+
+ /* also make sure the two blocks are the same length */
+ l = (int32)MIN((OFF_T)s->blength, len-offset);
+ if (l != s->sums[i].len)
+ continue;
+
+ if (DEBUG_GTE(DELTASUM, 3)) {
+ rprintf(FINFO,
+ "potential match at %s i=%ld sum=%08x\n",
+ big_num(offset), (long)i, sum);
+ }
+
+ if (!done_csum2) {
+ map = (schar *)map_ptr(buf,offset,l);
+ get_checksum2((char *)map,l,sum2);
+ done_csum2 = 1;
+ }
+
+ if (memcmp(sum2,s->sums[i].sum2,s->s2length) != 0) {
+ false_alarms++;
+ continue;
+ }
+
+ /* When updating in-place, the best possible match is
+ * one with an identical offset, so we prefer that over
+ * the adjacent want_i optimization. */
+ if (updating_basis_file) {
+ /* All the generator's chunks start at blength boundaries. */
+ while (aligned_offset < offset) {
+ aligned_offset += s->blength;
+ aligned_i++;
+ }
+ if ((offset == aligned_offset
+ || (sum == 0 && l == s->blength && aligned_offset + l <= len))
+ && aligned_i < s->count) {
+ if (i != aligned_i) {
+ if (sum != s->sums[aligned_i].sum1
+ || l != s->sums[aligned_i].len
+ || memcmp(sum2, s->sums[aligned_i].sum2, s->s2length) != 0)
+ goto check_want_i;
+ i = aligned_i;
+ }
+ if (offset != aligned_offset) {
+ /* We've matched some zeros in a spot that is also zeros
+ * further along in the basis file, if we find zeros ahead
+ * in the sender's file, we'll output enough literal data
+ * to re-align with the basis file, and get back to seeking
+ * instead of writing. */
+ backup = (int32)(aligned_offset - last_match);
+ if (backup < 0)
+ backup = 0;
+ map = (schar *)map_ptr(buf, aligned_offset - backup, l + backup)
+ + backup;
+ sum = get_checksum1((char *)map, l);
+ if (sum != s->sums[i].sum1)
+ goto check_want_i;
+ get_checksum2((char *)map, l, sum2);
+ if (memcmp(sum2, s->sums[i].sum2, s->s2length) != 0)
+ goto check_want_i;
+ /* OK, we have a re-alignment match. Bump the offset
+ * forward to the new match point. */
+ offset = aligned_offset;
+ }
+ /* This identical chunk is in the same spot in the old and new file. */
+ s->sums[i].flags |= SUMFLG_SAME_OFFSET;
+ want_i = i;
+ }
+ }
+
+ check_want_i:
+ /* we've found a match, but now check to see
+ * if want_i can hint at a better match. */
+ if (i != want_i && want_i < s->count
+ && (!updating_basis_file || s->sums[want_i].offset >= offset
+ || s->sums[want_i].flags & SUMFLG_SAME_OFFSET)
+ && sum == s->sums[want_i].sum1
+ && memcmp(sum2, s->sums[want_i].sum2, s->s2length) == 0) {
+ /* we've found an adjacent match - the RLL coder
+ * will be happy */
+ i = want_i;
+ }
+ want_i = i + 1;
+
+ matched(f,s,buf,offset,i);
+ offset += s->sums[i].len - 1;
+ k = (int32)MIN((OFF_T)s->blength, len-offset);
+ map = (schar *)map_ptr(buf, offset, k);
+ sum = get_checksum1((char *)map, k);
+ s1 = sum & 0xFFFF;
+ s2 = sum >> 16;
+ matches++;
+ break;
+ } while ((i = s->sums[i].chain) >= 0);
+
+ null_hash:
+ backup = (int32)(offset - last_match);
+ /* We sometimes read 1 byte prior to last_match... */
+ if (backup < 0)
+ backup = 0;
+
+ /* Trim off the first byte from the checksum */
+ more = offset + k < len;
+ map = (schar *)map_ptr(buf, offset - backup, k + more + backup)
+ + backup;
+ s1 -= map[0] + CHAR_OFFSET;
+ s2 -= k * (map[0]+CHAR_OFFSET);
+
+ /* Add on the next byte (if there is one) to the checksum */
+ if (more) {
+ s1 += map[k] + CHAR_OFFSET;
+ s2 += s1;
+ } else
+ --k;
+
+ /* By matching early we avoid re-reading the
+ data 3 times in the case where a token
+ match comes a long way after last
+ match. The 3 reads are caused by the
+ running match, the checksum update and the
+ literal send. */
+ if (backup >= s->blength+CHUNK_SIZE && end-offset > CHUNK_SIZE)
+ matched(f, s, buf, offset - s->blength, -2);
+ } while (++offset < end);
+
+ matched(f, s, buf, len, -1);
+ map_ptr(buf, len-1, 1);
+}
+
+
+/**
+ * Scan through a origin file, looking for sections that match
+ * checksums from the generator, and transmit either literal or token
+ * data.
+ *
+ * Also calculates the MD4 checksum of the whole file, using the md
+ * accumulator. This is transmitted with the file as protection
+ * against corruption on the wire.
+ *
+ * @param s Checksums received from the generator. If s->count ==
+ * 0, then there are actually no checksums for this file.
+ *
+ * @param len Length of the file to send.
+ **/
+void match_sums(int f, struct sum_struct *s, struct map_struct *buf, OFF_T len)
+{
+ last_match = 0;
+ false_alarms = 0;
+ hash_hits = 0;
+ matches = 0;
+ data_transfer = 0;
+
+ sum_init(checksum_seed);
+
+ if (append_mode > 0) {
+ if (append_mode == 2) {
+ OFF_T j = 0;
+ for (j = CHUNK_SIZE; j < s->flength; j += CHUNK_SIZE) {
+ if (buf && INFO_GTE(PROGRESS, 1))
+ show_progress(last_match, buf->file_size);
+ sum_update(map_ptr(buf, last_match, CHUNK_SIZE),
+ CHUNK_SIZE);
+ last_match = j;
+ }
+ if (last_match < s->flength) {
+ int32 n = (int32)(s->flength - last_match);
+ if (buf && INFO_GTE(PROGRESS, 1))
+ show_progress(last_match, buf->file_size);
+ sum_update(map_ptr(buf, last_match, n), n);
+ }
+ }
+ last_match = s->flength;
+ s->count = 0;
+ }
+
+ if (len > 0 && s->count > 0) {
+ build_hash_table(s);
+
+ if (DEBUG_GTE(DELTASUM, 2))
+ rprintf(FINFO,"built hash table\n");
+
+ hash_search(f, s, buf, len);
+
+ if (DEBUG_GTE(DELTASUM, 2))
+ rprintf(FINFO,"done hash search\n");
+ } else {
+ OFF_T j;
+ /* by doing this in pieces we avoid too many seeks */
+ for (j = last_match + CHUNK_SIZE; j < len; j += CHUNK_SIZE)
+ matched(f, s, buf, j, -2);
+ matched(f, s, buf, len, -1);
+ }
+
+ if (sum_end(sender_file_sum) != checksum_len)
+ overflow_exit("checksum_len"); /* Impossible... */
+
+ /* If we had a read error, send a bad checksum. We use all bits
+ * off as long as the checksum doesn't happen to be that, in
+ * which case we turn the last 0 bit into a 1. */
+ if (buf && buf->status != 0) {
+ int i;
+ for (i = 0; i < checksum_len && sender_file_sum[i] == 0; i++) {}
+ memset(sender_file_sum, 0, checksum_len);
+ if (i == checksum_len)
+ sender_file_sum[i-1]++;
+ }
+
+ if (DEBUG_GTE(DELTASUM, 2))
+ rprintf(FINFO,"sending file_sum\n");
+ write_buf(f, sender_file_sum, checksum_len);
+
+ if (DEBUG_GTE(DELTASUM, 2)) {
+ rprintf(FINFO, "false_alarms=%d hash_hits=%d matches=%d\n",
+ false_alarms, hash_hits, matches);
+ }
+
+ total_hash_hits += hash_hits;
+ total_false_alarms += false_alarms;
+ total_matches += matches;
+ stats.literal_data += data_transfer;
+}
+
+void match_report(void)
+{
+ if (!DEBUG_GTE(DELTASUM, 1))
+ return;
+
+ rprintf(FINFO,
+ "total: matches=%d hash_hits=%d false_alarms=%d data=%s\n",
+ total_matches, total_hash_hits, total_false_alarms,
+ big_num(stats.literal_data));
+}
diff --git a/rsync/mkproto.pl b/rsync/mkproto.pl
new file mode 100644
index 0000000..cdeb2ea
--- /dev/null
+++ b/rsync/mkproto.pl
@@ -0,0 +1,48 @@
+# generate prototypes for rsync
+
+$old_protos = '';
+if (open(IN, 'proto.h')) {
+ $old_protos = join('', );
+ close IN;
+}
+
+%FN_MAP = (
+ BOOL => 'BOOL ',
+ CHAR => 'char ',
+ INTEGER => 'int ',
+ STRING => 'char *',
+);
+
+$inheader = 0;
+$protos = qq|/* This file is automatically generated with "make proto". DO NOT EDIT */\n\n|;
+
+while (<>) {
+ if ($inheader) {
+ if (/[)][ \t]*$/) {
+ $inheader = 0;
+ s/$/;/;
+ }
+ $protos .= $_;
+ } elsif (/^FN_(LOCAL|GLOBAL)_([^(]+)\(([^,()]+)/) {
+ $ret = $FN_MAP{$2};
+ $func = $3;
+ $arg = $1 eq 'LOCAL' ? 'int module_id' : 'void';
+ $protos .= "$ret$func($arg);\n";
+ } elsif (/^static|^extern/ || /[;]/ || !/^[A-Za-z][A-Za-z0-9_]* /) {
+ ;
+ } elsif (/[(].*[)][ \t]*$/) {
+ s/$/;/;
+ $protos .= $_;
+ } elsif (/[(]/) {
+ $inheader = 1;
+ $protos .= $_;
+ }
+}
+
+if ($old_protos ne $protos) {
+ open(OUT, '>proto.h') or die $!;
+ print OUT $protos;
+ close OUT;
+}
+
+open(OUT, '>proto.h-tstamp') and close OUT;
diff --git a/rsync/options.c b/rsync/options.c
new file mode 100644
index 0000000..62dfe4f
--- /dev/null
+++ b/rsync/options.c
@@ -0,0 +1,2880 @@
+/*
+ * Command-line (and received via daemon-socket) option parsing.
+ *
+ * Copyright (C) 1998-2001 Andrew Tridgell
+ * Copyright (C) 2000, 2001, 2002 Martin Pool
+ * Copyright (C) 2002-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "itypes.h"
+#include
+#include
+
+extern int module_id;
+extern int local_server;
+extern int sanitize_paths;
+extern int daemon_over_rsh;
+extern unsigned int module_dirlen;
+extern filter_rule_list filter_list;
+extern filter_rule_list daemon_filter_list;
+
+#define NOT_SPECIFIED (-42)
+
+int make_backups = 0;
+
+/**
+ * If 1, send the whole file as literal data rather than trying to
+ * create an incremental diff.
+ *
+ * If -1, then look at whether we're local or remote and go by that.
+ *
+ * @sa disable_deltas_p()
+ **/
+int whole_file = -1;
+
+int append_mode = 0;
+int keep_dirlinks = 0;
+int copy_dirlinks = 0;
+int copy_links = 0;
+int preserve_links = 0;
+int preserve_hard_links = 0;
+int preserve_acls = 0;
+int preserve_xattrs = 0;
+int preserve_perms = 0;
+int preserve_executability = 0;
+int preserve_devices = 0;
+int preserve_specials = 0;
+int preserve_uid = 0;
+int preserve_gid = 0;
+int preserve_times = 0;
+int update_only = 0;
+int cvs_exclude = 0;
+int dry_run = 0;
+int do_xfers = 1;
+int ignore_times = 0;
+int delete_mode = 0;
+int delete_during = 0;
+int delete_before = 0;
+int delete_after = 0;
+int delete_excluded = 0;
+int remove_source_files = 0;
+int one_file_system = 0;
+int protocol_version = PROTOCOL_VERSION;
+int sparse_files = 0;
+int preallocate_files = 0;
+int do_compression = 0;
+int def_compress_level = NOT_SPECIFIED;
+int am_root = 0; /* 0 = normal, 1 = root, 2 = --super, -1 = --fake-super */
+int am_server = 0;
+int am_sender = 0;
+int am_starting_up = 1;
+int relative_paths = -1;
+int implied_dirs = 1;
+int missing_args = 0; /* 0 = FERROR_XFER, 1 = ignore, 2 = delete */
+int numeric_ids = 0;
+int msgs2stderr = 0;
+int allow_8bit_chars = 0;
+int force_delete = 0;
+int io_timeout = 0;
+int prune_empty_dirs = 0;
+int use_qsort = 0;
+char *files_from = NULL;
+int filesfrom_fd = -1;
+char *filesfrom_host = NULL;
+int eol_nulls = 0;
+int protect_args = -1;
+int human_readable = 1;
+int recurse = 0;
+int allow_inc_recurse = 1;
+int xfer_dirs = -1;
+int am_daemon = 0;
+int connect_timeout = 0;
+int keep_partial = 0;
+int safe_symlinks = 0;
+int copy_unsafe_links = 0;
+int munge_symlinks = 0;
+int size_only = 0;
+int daemon_bwlimit = 0;
+int bwlimit = 0;
+int fuzzy_basis = 0;
+size_t bwlimit_writemax = 0;
+int ignore_existing = 0;
+int ignore_non_existing = 0;
+int need_messages_from_generator = 0;
+int max_delete = INT_MIN;
+OFF_T max_size = -1;
+OFF_T min_size = -1;
+int ignore_errors = 0;
+int modify_window = 0;
+int blocking_io = -1;
+int checksum_seed = 0;
+int inplace = 0;
+int delay_updates = 0;
+long block_size = 0; /* "long" because popt can't set an int32. */
+char *skip_compress = NULL;
+item_list dparam_list = EMPTY_ITEM_LIST;
+
+/** Network address family. **/
+int default_af_hint
+#ifdef INET6
+ = 0; /* Any protocol */
+#else
+ = AF_INET; /* Must use IPv4 */
+# ifdef AF_INET6
+# undef AF_INET6
+# endif
+# define AF_INET6 AF_INET /* make -6 option a no-op */
+#endif
+
+/** Do not go into the background when run as --daemon. Good
+ * for debugging and required for running as a service on W32,
+ * or under Unix process-monitors. **/
+int no_detach
+#if defined _WIN32 || defined __WIN32__
+ = 1;
+#else
+ = 0;
+#endif
+
+int write_batch = 0;
+int read_batch = 0;
+int backup_dir_len = 0;
+int backup_suffix_len;
+unsigned int backup_dir_remainder;
+
+char *backup_suffix = NULL;
+char *tmpdir = NULL;
+char *partial_dir = NULL;
+char *basis_dir[MAX_BASIS_DIRS+1];
+char *config_file = NULL;
+char *shell_cmd = NULL;
+char *logfile_name = NULL;
+char *logfile_format = NULL;
+char *stdout_format = NULL;
+char *password_file = NULL;
+char *rsync_path = RSYNC_PATH;
+char *backup_dir = NULL;
+char backup_dir_buf[MAXPATHLEN];
+char *sockopts = NULL;
+char *usermap = NULL;
+char *groupmap = NULL;
+int rsync_port = 0;
+int compare_dest = 0;
+int copy_dest = 0;
+int link_dest = 0;
+int basis_dir_cnt = 0;
+char *dest_option = NULL;
+
+static int remote_option_alloc = 0;
+int remote_option_cnt = 0;
+const char **remote_options = NULL;
+
+int quiet = 0;
+int output_motd = 1;
+int log_before_transfer = 0;
+int stdout_format_has_i = 0;
+int stdout_format_has_o_or_i = 0;
+int logfile_format_has_i = 0;
+int logfile_format_has_o_or_i = 0;
+int always_checksum = 0;
+int list_only = 0;
+
+#define MAX_BATCH_NAME_LEN 256 /* Must be less than MAXPATHLEN-13 */
+char *batch_name = NULL;
+
+int need_unsorted_flist = 0;
+#ifdef ICONV_OPTION
+char *iconv_opt = ICONV_OPTION;
+#endif
+
+struct chmod_mode_struct *chmod_modes = NULL;
+
+static const char *debug_verbosity[] = {
+ /*0*/ NULL,
+ /*1*/ NULL,
+ /*2*/ "BIND,CMD,CONNECT,DEL,DELTASUM,DUP,FILTER,FLIST,ICONV",
+ /*3*/ "ACL,BACKUP,CONNECT2,DELTASUM2,DEL2,EXIT,FILTER2,FLIST2,FUZZY,GENR,OWN,RECV,SEND,TIME",
+ /*4*/ "CMD2,DELTASUM3,DEL3,EXIT2,FLIST3,ICONV2,OWN2,PROTO,TIME2",
+ /*5*/ "CHDIR,DELTASUM4,FLIST4,FUZZY2,HASH,HLINK",
+};
+
+#define MAX_VERBOSITY ((int)(sizeof debug_verbosity / sizeof debug_verbosity[0]) - 1)
+
+static const char *info_verbosity[1+MAX_VERBOSITY] = {
+ /*0*/ NULL,
+ /*1*/ "COPY,DEL,FLIST,MISC,NAME,STATS,SYMSAFE",
+ /*2*/ "BACKUP,MISC2,MOUNT,NAME2,REMOVE,SKIP",
+};
+
+#define MAX_OUT_LEVEL 4 /* The largest N allowed for any flagN word. */
+
+short info_levels[COUNT_INFO], debug_levels[COUNT_DEBUG];
+
+#define DEFAULT_PRIORITY 0 /* Default/implied/--verbose set values. */
+#define HELP_PRIORITY 1 /* The help output uses this level. */
+#define USER_PRIORITY 2 /* User-specified via --info or --debug */
+#define LIMIT_PRIORITY 3 /* Overriding priority when limiting values. */
+
+#define W_CLI (1<<0) /* client side */
+#define W_SRV (1<<1) /* server side */
+#define W_SND (1<<2) /* sending side */
+#define W_REC (1<<3) /* receiving side */
+
+struct output_struct {
+ char *name; /* The name of the info/debug flag. */
+ char *help; /* The description of the info/debug flag. */
+ uchar namelen; /* The length of the name string. */
+ uchar flag; /* The flag's value, for consistency check. */
+ uchar where; /* Bits indicating where the flag is used. */
+ uchar priority; /* See *_PRIORITY defines. */
+};
+
+#define INFO_WORD(flag, where, help) { #flag, help, sizeof #flag - 1, INFO_##flag, where, 0 }
+
+static struct output_struct info_words[COUNT_INFO+1] = {
+ INFO_WORD(BACKUP, W_REC, "Mention files backed up"),
+ INFO_WORD(COPY, W_REC, "Mention files copied locally on the receiving side"),
+ INFO_WORD(DEL, W_REC, "Mention deletions on the receiving side"),
+ INFO_WORD(FLIST, W_CLI, "Mention file-list receiving/sending (levels 1-2)"),
+ INFO_WORD(MISC, W_SND|W_REC, "Mention miscellaneous information (levels 1-2)"),
+ INFO_WORD(MOUNT, W_SND|W_REC, "Mention mounts that were found or skipped"),
+ INFO_WORD(NAME, W_SND|W_REC, "Mention 1) updated file/dir names, 2) unchanged names"),
+ INFO_WORD(PROGRESS, W_CLI, "Mention 1) per-file progress or 2) total transfer progress"),
+ INFO_WORD(REMOVE, W_SND, "Mention files removed on the sending side"),
+ INFO_WORD(SKIP, W_REC, "Mention files that are skipped due to options used"),
+ INFO_WORD(STATS, W_CLI|W_SRV, "Mention statistics at end of run (levels 1-3)"),
+ INFO_WORD(SYMSAFE, W_SND|W_REC, "Mention symlinks that are unsafe"),
+ { NULL, "--info", 0, 0, 0, 0 }
+};
+
+#define DEBUG_WORD(flag, where, help) { #flag, help, sizeof #flag - 1, DEBUG_##flag, where, 0 }
+
+static struct output_struct debug_words[COUNT_DEBUG+1] = {
+ DEBUG_WORD(ACL, W_SND|W_REC, "Debug extra ACL info"),
+ DEBUG_WORD(BACKUP, W_REC, "Debug backup actions (levels 1-2)"),
+ DEBUG_WORD(BIND, W_CLI, "Debug socket bind actions"),
+ DEBUG_WORD(CHDIR, W_CLI|W_SRV, "Debug when the current directory changes"),
+ DEBUG_WORD(CONNECT, W_CLI, "Debug connection events (levels 1-2)"),
+ DEBUG_WORD(CMD, W_CLI, "Debug commands+options that are issued (levels 1-2)"),
+ DEBUG_WORD(DEL, W_REC, "Debug delete actions (levels 1-3)"),
+ DEBUG_WORD(DELTASUM, W_SND|W_REC, "Debug delta-transfer checksumming (levels 1-4)"),
+ DEBUG_WORD(DUP, W_REC, "Debug weeding of duplicate names"),
+ DEBUG_WORD(EXIT, W_CLI|W_SRV, "Debug exit events (levels 1-3)"),
+ DEBUG_WORD(FILTER, W_SND|W_REC, "Debug filter actions (levels 1-2)"),
+ DEBUG_WORD(FLIST, W_SND|W_REC, "Debug file-list operations (levels 1-4)"),
+ DEBUG_WORD(FUZZY, W_REC, "Debug fuzzy scoring (levels 1-2)"),
+ DEBUG_WORD(GENR, W_REC, "Debug generator functions"),
+ DEBUG_WORD(HASH, W_SND|W_REC, "Debug hashtable code"),
+ DEBUG_WORD(HLINK, W_SND|W_REC, "Debug hard-link actions (levels 1-3)"),
+ DEBUG_WORD(ICONV, W_CLI|W_SRV, "Debug iconv character conversions (levels 1-2)"),
+ DEBUG_WORD(IO, W_CLI|W_SRV, "Debug I/O routines (levels 1-4)"),
+ DEBUG_WORD(OWN, W_REC, "Debug ownership changes in users & groups (levels 1-2)"),
+ DEBUG_WORD(PROTO, W_CLI|W_SRV, "Debug protocol information"),
+ DEBUG_WORD(RECV, W_REC, "Debug receiver functions"),
+ DEBUG_WORD(SEND, W_SND, "Debug sender functions"),
+ DEBUG_WORD(TIME, W_REC, "Debug setting of modified times (levels 1-2)"),
+ { NULL, "--debug", 0, 0, 0, 0 }
+};
+
+static int verbose = 0;
+static int do_stats = 0;
+static int do_progress = 0;
+static int daemon_opt; /* sets am_daemon after option error-reporting */
+static int omit_dir_times = 0;
+static int omit_link_times = 0;
+static int F_option_cnt = 0;
+static int modify_window_set;
+static int itemize_changes = 0;
+static int refused_delete, refused_archive_part, refused_compress;
+static int refused_partial, refused_progress, refused_delete_before;
+static int refused_delete_during;
+static int refused_inplace, refused_no_iconv;
+static BOOL usermap_via_chown, groupmap_via_chown;
+#ifdef HAVE_SETVBUF
+static char *outbuf_mode;
+#endif
+static char *bwlimit_arg, *max_size_arg, *min_size_arg;
+static char tmp_partialdir[] = ".~tmp~";
+
+/** Local address to bind. As a character string because it's
+ * interpreted by the IPv6 layer: should be a numeric IP4 or IP6
+ * address, or a hostname. **/
+char *bind_address;
+
+static void output_item_help(struct output_struct *words);
+
+/* This constructs a string that represents all the options set for either
+ * the --info or --debug setting, skipping any implied options (by -v, etc.).
+ * This is used both when conveying the user's options to the server, and
+ * when the help output wants to tell the user what options are implied. */
+static char *make_output_option(struct output_struct *words, short *levels, uchar where)
+{
+ char *str = words == info_words ? "--info=" : "--debug=";
+ int j, counts[MAX_OUT_LEVEL+1], pos, skipped = 0, len = 0, max = 0, lev = 0;
+ int word_count = words == info_words ? COUNT_INFO : COUNT_DEBUG;
+ char *buf;
+
+ memset(counts, 0, sizeof counts);
+
+ for (j = 0; words[j].name; j++) {
+ if (words[j].flag != j) {
+ rprintf(FERROR, "rsync: internal error on %s%s: %d != %d\n",
+ words == info_words ? "INFO_" : "DEBUG_",
+ words[j].name, words[j].flag, j);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+ if (!(words[j].where & where))
+ continue;
+ if (words[j].priority == DEFAULT_PRIORITY) {
+ /* Implied items don't need to be mentioned. */
+ skipped++;
+ continue;
+ }
+ len += len ? 1 : strlen(str);
+ len += strlen(words[j].name);
+ len += levels[j] == 1 ? 0 : 1;
+
+ if (words[j].priority == HELP_PRIORITY)
+ continue; /* no abbreviating for help */
+
+ assert(levels[j] <= MAX_OUT_LEVEL);
+ if (++counts[levels[j]] > max) {
+ /* Determine which level has the most items. */
+ lev = levels[j];
+ max = counts[lev];
+ }
+ }
+
+ /* Sanity check the COUNT_* define against the length of the table. */
+ if (j != word_count) {
+ rprintf(FERROR, "rsync: internal error: %s is wrong! (%d != %d)\n",
+ words == info_words ? "COUNT_INFO" : "COUNT_DEBUG",
+ j, word_count);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+
+ if (!len)
+ return NULL;
+
+ len++;
+ if (!(buf = new_array(char, len)))
+ out_of_memory("make_output_option");
+ pos = 0;
+
+ if (skipped || max < 5)
+ lev = -1;
+ else {
+ if (lev == 0)
+ pos += snprintf(buf, len, "%sNONE", str);
+ else if (lev == 1)
+ pos += snprintf(buf, len, "%sALL", str);
+ else
+ pos += snprintf(buf, len, "%sALL%d", str, lev);
+ }
+
+ for (j = 0; words[j].name && pos < len; j++) {
+ if (words[j].priority == DEFAULT_PRIORITY || levels[j] == lev || !(words[j].where & where))
+ continue;
+ if (pos)
+ buf[pos++] = ',';
+ else
+ pos += strlcpy(buf+pos, str, len-pos);
+ if (pos < len)
+ pos += strlcpy(buf+pos, words[j].name, len-pos);
+ /* Level 1 is implied by the name alone. */
+ if (levels[j] != 1 && pos < len)
+ buf[pos++] = '0' + levels[j];
+ }
+
+ buf[pos] = '\0';
+
+ return buf;
+}
+
+static void parse_output_words(struct output_struct *words, short *levels,
+ const char *str, uchar priority)
+{
+ const char *s;
+ int j, len, lev;
+
+ if (!str)
+ return;
+
+ while (*str) {
+ if ((s = strchr(str, ',')) != NULL)
+ len = s++ - str;
+ else
+ len = strlen(str);
+ while (len && isDigit(str+len-1))
+ len--;
+ lev = isDigit(str+len) ? atoi(str+len) : 1;
+ if (lev > MAX_OUT_LEVEL)
+ lev = MAX_OUT_LEVEL;
+ if (len == 4 && strncasecmp(str, "help", 4) == 0) {
+ output_item_help(words);
+ exit_cleanup(0);
+ }
+ if (len == 4 && strncasecmp(str, "none", 4) == 0)
+ len = lev = 0;
+ else if (len == 3 && strncasecmp(str, "all", 3) == 0)
+ len = 0;
+ for (j = 0; words[j].name; j++) {
+ if (!len
+ || (len == words[j].namelen && strncasecmp(str, words[j].name, len) == 0)) {
+ if (priority >= words[j].priority) {
+ words[j].priority = priority;
+ levels[j] = lev;
+ }
+ if (len)
+ break;
+ }
+ }
+ if (len && !words[j].name) {
+ rprintf(FERROR, "Unknown %s item: \"%.*s\"\n",
+ words[j].help, len, str);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (!s)
+ break;
+ str = s;
+ }
+}
+
+/* Tell the user what all the info or debug flags mean. */
+static void output_item_help(struct output_struct *words)
+{
+ short *levels = words == info_words ? info_levels : debug_levels;
+ const char **verbosity = words == info_words ? info_verbosity : debug_verbosity;
+ char buf[128], *opt, *fmt = "%-10s %s\n";
+ int j;
+
+ reset_output_levels();
+
+ rprintf(FINFO, "Use OPT or OPT1 for level 1 output, OPT2 for level 2, etc.; OPT0 silences.\n");
+ rprintf(FINFO, "\n");
+ for (j = 0; words[j].name; j++)
+ rprintf(FINFO, fmt, words[j].name, words[j].help);
+ rprintf(FINFO, "\n");
+
+ snprintf(buf, sizeof buf, "Set all %s options (e.g. all%d)",
+ words[j].help, MAX_OUT_LEVEL);
+ rprintf(FINFO, fmt, "ALL", buf);
+
+ snprintf(buf, sizeof buf, "Silence all %s options (same as all0)",
+ words[j].help);
+ rprintf(FINFO, fmt, "NONE", buf);
+
+ rprintf(FINFO, fmt, "HELP", "Output this help message");
+ rprintf(FINFO, "\n");
+ rprintf(FINFO, "Options added for each increase in verbose level:\n");
+
+ for (j = 1; j <= MAX_VERBOSITY; j++) {
+ parse_output_words(words, levels, verbosity[j], HELP_PRIORITY);
+ opt = make_output_option(words, levels, W_CLI|W_SRV|W_SND|W_REC);
+ if (opt) {
+ rprintf(FINFO, "%d) %s\n", j, strchr(opt, '=')+1);
+ free(opt);
+ }
+ reset_output_levels();
+ }
+}
+
+/* The --verbose option now sets info+debug flags. */
+static void set_output_verbosity(int level, uchar priority)
+{
+ int j;
+
+ if (level > MAX_VERBOSITY)
+ level = MAX_VERBOSITY;
+
+ for (j = 1; j <= level; j++) {
+ parse_output_words(info_words, info_levels, info_verbosity[j], priority);
+ parse_output_words(debug_words, debug_levels, debug_verbosity[j], priority);
+ }
+}
+
+/* Limit the info+debug flag levels given a verbose-option level limit. */
+void limit_output_verbosity(int level)
+{
+ short info_limits[COUNT_INFO], debug_limits[COUNT_DEBUG];
+ int j;
+
+ if (level > MAX_VERBOSITY)
+ return;
+
+ memset(info_limits, 0, sizeof info_limits);
+ memset(debug_limits, 0, sizeof debug_limits);
+
+ /* Compute the level limits in the above arrays. */
+ for (j = 1; j <= level; j++) {
+ parse_output_words(info_words, info_limits, info_verbosity[j], LIMIT_PRIORITY);
+ parse_output_words(debug_words, debug_limits, debug_verbosity[j], LIMIT_PRIORITY);
+ }
+
+ for (j = 0; j < COUNT_INFO; j++) {
+ if (info_levels[j] > info_limits[j])
+ info_levels[j] = info_limits[j];
+ }
+
+ for (j = 0; j < COUNT_DEBUG; j++) {
+ if (debug_levels[j] > debug_limits[j])
+ debug_levels[j] = debug_limits[j];
+ }
+}
+
+void reset_output_levels(void)
+{
+ int j;
+
+ memset(info_levels, 0, sizeof info_levels);
+ memset(debug_levels, 0, sizeof debug_levels);
+
+ for (j = 0; j < COUNT_INFO; j++)
+ info_words[j].priority = DEFAULT_PRIORITY;
+
+ for (j = 0; j < COUNT_DEBUG; j++)
+ debug_words[j].priority = DEFAULT_PRIORITY;
+}
+
+void negate_output_levels(void)
+{
+ int j;
+
+ for (j = 0; j < COUNT_INFO; j++)
+ info_levels[j] *= -1;
+
+ for (j = 0; j < COUNT_DEBUG; j++)
+ debug_levels[j] *= -1;
+}
+
+static void print_rsync_version(enum logcode f)
+{
+ char *subprotocol = "";
+ char const *got_socketpair = "no ";
+ char const *have_inplace = "no ";
+ char const *hardlinks = "no ";
+ char const *prealloc = "no ";
+ char const *symtimes = "no ";
+ char const *acls = "no ";
+ char const *xattrs = "no ";
+ char const *links = "no ";
+ char const *iconv = "no ";
+ char const *ipv6 = "no ";
+ STRUCT_STAT *dumstat;
+
+#if SUBPROTOCOL_VERSION != 0
+ if (asprintf(&subprotocol, ".PR%d", SUBPROTOCOL_VERSION) < 0)
+ out_of_memory("print_rsync_version");
+#endif
+#ifdef HAVE_SOCKETPAIR
+ got_socketpair = "";
+#endif
+#ifdef HAVE_FTRUNCATE
+ have_inplace = "";
+#endif
+#ifdef SUPPORT_HARD_LINKS
+ hardlinks = "";
+#endif
+#ifdef SUPPORT_PREALLOCATION
+ prealloc = "";
+#endif
+#ifdef SUPPORT_ACLS
+ acls = "";
+#endif
+#ifdef SUPPORT_XATTRS
+ xattrs = "";
+#endif
+#ifdef SUPPORT_LINKS
+ links = "";
+#endif
+#ifdef INET6
+ ipv6 = "";
+#endif
+#ifdef ICONV_OPTION
+ iconv = "";
+#endif
+#ifdef CAN_SET_SYMLINK_TIMES
+ symtimes = "";
+#endif
+
+ rprintf(f, "%s version %s protocol version %d%s\n",
+ RSYNC_NAME, RSYNC_VERSION, PROTOCOL_VERSION, subprotocol);
+ rprintf(f, "Copyright (C) 1996-2014 by Andrew Tridgell, Wayne Davison, and others.\n");
+ rprintf(f, "Web site: http://rsync.samba.org/\n");
+ rprintf(f, "Capabilities:\n");
+ rprintf(f, " %d-bit files, %d-bit inums, %d-bit timestamps, %d-bit long ints,\n",
+ (int)(sizeof (OFF_T) * 8),
+ (int)(sizeof dumstat->st_ino * 8), /* Don't check ino_t! */
+ (int)(sizeof (time_t) * 8),
+ (int)(sizeof (int64) * 8));
+ rprintf(f, " %ssocketpairs, %shardlinks, %ssymlinks, %sIPv6, batchfiles, %sinplace,\n",
+ got_socketpair, hardlinks, links, ipv6, have_inplace);
+ rprintf(f, " %sappend, %sACLs, %sxattrs, %siconv, %ssymtimes, %sprealloc\n",
+ have_inplace, acls, xattrs, iconv, symtimes, prealloc);
+
+#ifdef MAINTAINER_MODE
+ rprintf(f, "Panic Action: \"%s\"\n", get_panic_action());
+#endif
+
+#if SIZEOF_INT64 < 8
+ rprintf(f, "WARNING: no 64-bit integers on this platform!\n");
+#endif
+ if (sizeof (int64) != SIZEOF_INT64) {
+ rprintf(f,
+ "WARNING: size mismatch in SIZEOF_INT64 define (%d != %d)\n",
+ (int) SIZEOF_INT64, (int) sizeof (int64));
+ }
+
+ rprintf(f,"\n");
+ rprintf(f,"rsync comes with ABSOLUTELY NO WARRANTY. This is free software, and you\n");
+ rprintf(f,"are welcome to redistribute it under certain conditions. See the GNU\n");
+ rprintf(f,"General Public Licence for details.\n");
+}
+
+
+void usage(enum logcode F)
+{
+ print_rsync_version(F);
+
+ rprintf(F,"\n");
+ rprintf(F,"rsync is a file transfer program capable of efficient remote update\n");
+ rprintf(F,"via a fast differencing algorithm.\n");
+
+ rprintf(F,"\n");
+ rprintf(F,"Usage: rsync [OPTION]... SRC [SRC]... DEST\n");
+ rprintf(F," or rsync [OPTION]... SRC [SRC]... [USER@]HOST:DEST\n");
+ rprintf(F," or rsync [OPTION]... SRC [SRC]... [USER@]HOST::DEST\n");
+ rprintf(F," or rsync [OPTION]... SRC [SRC]... rsync://[USER@]HOST[:PORT]/DEST\n");
+ rprintf(F," or rsync [OPTION]... [USER@]HOST:SRC [DEST]\n");
+ rprintf(F," or rsync [OPTION]... [USER@]HOST::SRC [DEST]\n");
+ rprintf(F," or rsync [OPTION]... rsync://[USER@]HOST[:PORT]/SRC [DEST]\n");
+ rprintf(F,"The ':' usages connect via remote shell, while '::' & 'rsync://' usages connect\n");
+ rprintf(F,"to an rsync daemon, and require SRC or DEST to start with a module name.\n");
+ rprintf(F,"\n");
+ rprintf(F,"Options\n");
+ rprintf(F," -v, --verbose increase verbosity\n");
+ rprintf(F," --info=FLAGS fine-grained informational verbosity\n");
+ rprintf(F," --debug=FLAGS fine-grained debug verbosity\n");
+ rprintf(F," --msgs2stderr special output handling for debugging\n");
+ rprintf(F," -q, --quiet suppress non-error messages\n");
+ rprintf(F," --no-motd suppress daemon-mode MOTD (see manpage caveat)\n");
+ rprintf(F," -c, --checksum skip based on checksum, not mod-time & size\n");
+ rprintf(F," -a, --archive archive mode; equals -rlptgoD (no -H,-A,-X)\n");
+ rprintf(F," --no-OPTION turn off an implied OPTION (e.g. --no-D)\n");
+ rprintf(F," -r, --recursive recurse into directories\n");
+ rprintf(F," -R, --relative use relative path names\n");
+ rprintf(F," --no-implied-dirs don't send implied dirs with --relative\n");
+ rprintf(F," -b, --backup make backups (see --suffix & --backup-dir)\n");
+ rprintf(F," --backup-dir=DIR make backups into hierarchy based in DIR\n");
+ rprintf(F," --suffix=SUFFIX set backup suffix (default %s w/o --backup-dir)\n",BACKUP_SUFFIX);
+ rprintf(F," -u, --update skip files that are newer on the receiver\n");
+ rprintf(F," --inplace update destination files in-place (SEE MAN PAGE)\n");
+ rprintf(F," --append append data onto shorter files\n");
+ rprintf(F," --append-verify like --append, but with old data in file checksum\n");
+ rprintf(F," -d, --dirs transfer directories without recursing\n");
+ rprintf(F," -l, --links copy symlinks as symlinks\n");
+ rprintf(F," -L, --copy-links transform symlink into referent file/dir\n");
+ rprintf(F," --copy-unsafe-links only \"unsafe\" symlinks are transformed\n");
+ rprintf(F," --safe-links ignore symlinks that point outside the source tree\n");
+ rprintf(F," --munge-links munge symlinks to make them safer (but unusable)\n");
+ rprintf(F," -k, --copy-dirlinks transform symlink to a dir into referent dir\n");
+ rprintf(F," -K, --keep-dirlinks treat symlinked dir on receiver as dir\n");
+ rprintf(F," -H, --hard-links preserve hard links\n");
+ rprintf(F," -p, --perms preserve permissions\n");
+ rprintf(F," -E, --executability preserve the file's executability\n");
+ rprintf(F," --chmod=CHMOD affect file and/or directory permissions\n");
+#ifdef SUPPORT_ACLS
+ rprintf(F," -A, --acls preserve ACLs (implies --perms)\n");
+#endif
+#ifdef SUPPORT_XATTRS
+ rprintf(F," -X, --xattrs preserve extended attributes\n");
+#endif
+ rprintf(F," -o, --owner preserve owner (super-user only)\n");
+ rprintf(F," -g, --group preserve group\n");
+ rprintf(F," --devices preserve device files (super-user only)\n");
+ rprintf(F," --specials preserve special files\n");
+ rprintf(F," -D same as --devices --specials\n");
+ rprintf(F," -t, --times preserve modification times\n");
+ rprintf(F," -O, --omit-dir-times omit directories from --times\n");
+ rprintf(F," -J, --omit-link-times omit symlinks from --times\n");
+ rprintf(F," --super receiver attempts super-user activities\n");
+#ifdef SUPPORT_XATTRS
+ rprintf(F," --fake-super store/recover privileged attrs using xattrs\n");
+#endif
+ rprintf(F," -S, --sparse handle sparse files efficiently\n");
+#ifdef SUPPORT_PREALLOCATION
+ rprintf(F," --preallocate allocate dest files before writing them\n");
+#else
+ rprintf(F," --preallocate pre-allocate dest files on remote receiver\n");
+#endif
+ rprintf(F," -n, --dry-run perform a trial run with no changes made\n");
+ rprintf(F," -W, --whole-file copy files whole (without delta-xfer algorithm)\n");
+ rprintf(F," -x, --one-file-system don't cross filesystem boundaries\n");
+ rprintf(F," -B, --block-size=SIZE force a fixed checksum block-size\n");
+ rprintf(F," -e, --rsh=COMMAND specify the remote shell to use\n");
+ rprintf(F," --rsync-path=PROGRAM specify the rsync to run on the remote machine\n");
+ rprintf(F," --existing skip creating new files on receiver\n");
+ rprintf(F," --ignore-existing skip updating files that already exist on receiver\n");
+ rprintf(F," --remove-source-files sender removes synchronized files (non-dirs)\n");
+ rprintf(F," --del an alias for --delete-during\n");
+ rprintf(F," --delete delete extraneous files from destination dirs\n");
+ rprintf(F," --delete-before receiver deletes before transfer, not during\n");
+ rprintf(F," --delete-during receiver deletes during the transfer\n");
+ rprintf(F," --delete-delay find deletions during, delete after\n");
+ rprintf(F," --delete-after receiver deletes after transfer, not during\n");
+ rprintf(F," --delete-excluded also delete excluded files from destination dirs\n");
+ rprintf(F," --ignore-missing-args ignore missing source args without error\n");
+ rprintf(F," --delete-missing-args delete missing source args from destination\n");
+ rprintf(F," --ignore-errors delete even if there are I/O errors\n");
+ rprintf(F," --force force deletion of directories even if not empty\n");
+ rprintf(F," --max-delete=NUM don't delete more than NUM files\n");
+ rprintf(F," --max-size=SIZE don't transfer any file larger than SIZE\n");
+ rprintf(F," --min-size=SIZE don't transfer any file smaller than SIZE\n");
+ rprintf(F," --partial keep partially transferred files\n");
+ rprintf(F," --partial-dir=DIR put a partially transferred file into DIR\n");
+ rprintf(F," --delay-updates put all updated files into place at transfer's end\n");
+ rprintf(F," -m, --prune-empty-dirs prune empty directory chains from the file-list\n");
+ rprintf(F," --numeric-ids don't map uid/gid values by user/group name\n");
+ rprintf(F," --usermap=STRING custom username mapping\n");
+ rprintf(F," --groupmap=STRING custom groupname mapping\n");
+ rprintf(F," --chown=USER:GROUP simple username/groupname mapping\n");
+ rprintf(F," --timeout=SECONDS set I/O timeout in seconds\n");
+ rprintf(F," --contimeout=SECONDS set daemon connection timeout in seconds\n");
+ rprintf(F," -I, --ignore-times don't skip files that match in size and mod-time\n");
+ rprintf(F," -M, --remote-option=OPTION send OPTION to the remote side only\n");
+ rprintf(F," --size-only skip files that match in size\n");
+ rprintf(F," --modify-window=NUM compare mod-times with reduced accuracy\n");
+ rprintf(F," -T, --temp-dir=DIR create temporary files in directory DIR\n");
+ rprintf(F," -y, --fuzzy find similar file for basis if no dest file\n");
+ rprintf(F," --compare-dest=DIR also compare destination files relative to DIR\n");
+ rprintf(F," --copy-dest=DIR ... and include copies of unchanged files\n");
+ rprintf(F," --link-dest=DIR hardlink to files in DIR when unchanged\n");
+ rprintf(F," -z, --compress compress file data during the transfer\n");
+ rprintf(F," --compress-level=NUM explicitly set compression level\n");
+ rprintf(F," --skip-compress=LIST skip compressing files with a suffix in LIST\n");
+ rprintf(F," -C, --cvs-exclude auto-ignore files the same way CVS does\n");
+ rprintf(F," -f, --filter=RULE add a file-filtering RULE\n");
+ rprintf(F," -F same as --filter='dir-merge /.rsync-filter'\n");
+ rprintf(F," repeated: --filter='- .rsync-filter'\n");
+ rprintf(F," --exclude=PATTERN exclude files matching PATTERN\n");
+ rprintf(F," --exclude-from=FILE read exclude patterns from FILE\n");
+ rprintf(F," --include=PATTERN don't exclude files matching PATTERN\n");
+ rprintf(F," --include-from=FILE read include patterns from FILE\n");
+ rprintf(F," --files-from=FILE read list of source-file names from FILE\n");
+ rprintf(F," -0, --from0 all *-from/filter files are delimited by 0s\n");
+ rprintf(F," -s, --protect-args no space-splitting; only wildcard special-chars\n");
+ rprintf(F," --address=ADDRESS bind address for outgoing socket to daemon\n");
+ rprintf(F," --port=PORT specify double-colon alternate port number\n");
+ rprintf(F," --sockopts=OPTIONS specify custom TCP options\n");
+ rprintf(F," --blocking-io use blocking I/O for the remote shell\n");
+ rprintf(F," --stats give some file-transfer stats\n");
+ rprintf(F," -8, --8-bit-output leave high-bit chars unescaped in output\n");
+ rprintf(F," -h, --human-readable output numbers in a human-readable format\n");
+ rprintf(F," --progress show progress during transfer\n");
+ rprintf(F," -P same as --partial --progress\n");
+ rprintf(F," -i, --itemize-changes output a change-summary for all updates\n");
+ rprintf(F," --out-format=FORMAT output updates using the specified FORMAT\n");
+ rprintf(F," --log-file=FILE log what we're doing to the specified FILE\n");
+ rprintf(F," --log-file-format=FMT log updates using the specified FMT\n");
+ rprintf(F," --password-file=FILE read daemon-access password from FILE\n");
+ rprintf(F," --list-only list the files instead of copying them\n");
+ rprintf(F," --bwlimit=RATE limit socket I/O bandwidth\n");
+#ifdef HAVE_SETVBUF
+ rprintf(F," --outbuf=N|L|B set output buffering to None, Line, or Block\n");
+#endif
+ rprintf(F," --write-batch=FILE write a batched update to FILE\n");
+ rprintf(F," --only-write-batch=FILE like --write-batch but w/o updating destination\n");
+ rprintf(F," --read-batch=FILE read a batched update from FILE\n");
+ rprintf(F," --protocol=NUM force an older protocol version to be used\n");
+#ifdef ICONV_OPTION
+ rprintf(F," --iconv=CONVERT_SPEC request charset conversion of filenames\n");
+#endif
+ rprintf(F," --checksum-seed=NUM set block/file checksum seed (advanced)\n");
+ rprintf(F," -4, --ipv4 prefer IPv4\n");
+ rprintf(F," -6, --ipv6 prefer IPv6\n");
+ rprintf(F," --version print version number\n");
+ rprintf(F,"(-h) --help show this help (-h is --help only if used alone)\n");
+
+ rprintf(F,"\n");
+ rprintf(F,"Use \"rsync --daemon --help\" to see the daemon-mode command-line options.\n");
+ rprintf(F,"Please see the rsync(1) and rsyncd.conf(5) man pages for full documentation.\n");
+ rprintf(F,"See http://rsync.samba.org/ for updates, bug reports, and answers\n");
+}
+
+enum {OPT_VERSION = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM,
+ OPT_FILTER, OPT_COMPARE_DEST, OPT_COPY_DEST, OPT_LINK_DEST, OPT_HELP,
+ OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW, OPT_MIN_SIZE, OPT_CHMOD,
+ OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_ONLY_WRITE_BATCH, OPT_MAX_SIZE,
+ OPT_NO_D, OPT_APPEND, OPT_NO_ICONV, OPT_INFO, OPT_DEBUG,
+ OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_BWLIMIT,
+ OPT_SERVER, OPT_REFUSED_BASE = 9000};
+
+static struct poptOption long_options[] = {
+ /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
+ {"help", 0, POPT_ARG_NONE, 0, OPT_HELP, 0, 0 },
+ {"version", 0, POPT_ARG_NONE, 0, OPT_VERSION, 0, 0},
+ {"verbose", 'v', POPT_ARG_NONE, 0, 'v', 0, 0 },
+ {"no-verbose", 0, POPT_ARG_VAL, &verbose, 0, 0, 0 },
+ {"no-v", 0, POPT_ARG_VAL, &verbose, 0, 0, 0 },
+ {"info", 0, POPT_ARG_STRING, 0, OPT_INFO, 0, 0 },
+ {"debug", 0, POPT_ARG_STRING, 0, OPT_DEBUG, 0, 0 },
+ {"msgs2stderr", 0, POPT_ARG_NONE, &msgs2stderr, 0, 0, 0 },
+ {"quiet", 'q', POPT_ARG_NONE, 0, 'q', 0, 0 },
+ {"motd", 0, POPT_ARG_VAL, &output_motd, 1, 0, 0 },
+ {"no-motd", 0, POPT_ARG_VAL, &output_motd, 0, 0, 0 },
+ {"stats", 0, POPT_ARG_NONE, &do_stats, 0, 0, 0 },
+ {"human-readable", 'h', POPT_ARG_NONE, 0, 'h', 0, 0},
+ {"no-human-readable",0, POPT_ARG_VAL, &human_readable, 0, 0, 0},
+ {"no-h", 0, POPT_ARG_VAL, &human_readable, 0, 0, 0},
+ {"dry-run", 'n', POPT_ARG_NONE, &dry_run, 0, 0, 0 },
+ {"archive", 'a', POPT_ARG_NONE, 0, 'a', 0, 0 },
+ {"recursive", 'r', POPT_ARG_VAL, &recurse, 2, 0, 0 },
+ {"no-recursive", 0, POPT_ARG_VAL, &recurse, 0, 0, 0 },
+ {"no-r", 0, POPT_ARG_VAL, &recurse, 0, 0, 0 },
+ {"inc-recursive", 0, POPT_ARG_VAL, &allow_inc_recurse, 1, 0, 0 },
+ {"no-inc-recursive", 0, POPT_ARG_VAL, &allow_inc_recurse, 0, 0, 0 },
+ {"i-r", 0, POPT_ARG_VAL, &allow_inc_recurse, 1, 0, 0 },
+ {"no-i-r", 0, POPT_ARG_VAL, &allow_inc_recurse, 0, 0, 0 },
+ {"dirs", 'd', POPT_ARG_VAL, &xfer_dirs, 2, 0, 0 },
+ {"no-dirs", 0, POPT_ARG_VAL, &xfer_dirs, 0, 0, 0 },
+ {"no-d", 0, POPT_ARG_VAL, &xfer_dirs, 0, 0, 0 },
+ {"old-dirs", 0, POPT_ARG_VAL, &xfer_dirs, 4, 0, 0 },
+ {"old-d", 0, POPT_ARG_VAL, &xfer_dirs, 4, 0, 0 },
+ {"perms", 'p', POPT_ARG_VAL, &preserve_perms, 1, 0, 0 },
+ {"no-perms", 0, POPT_ARG_VAL, &preserve_perms, 0, 0, 0 },
+ {"no-p", 0, POPT_ARG_VAL, &preserve_perms, 0, 0, 0 },
+ {"executability", 'E', POPT_ARG_NONE, &preserve_executability, 0, 0, 0 },
+ {"acls", 'A', POPT_ARG_NONE, 0, 'A', 0, 0 },
+ {"no-acls", 0, POPT_ARG_VAL, &preserve_acls, 0, 0, 0 },
+ {"no-A", 0, POPT_ARG_VAL, &preserve_acls, 0, 0, 0 },
+ {"xattrs", 'X', POPT_ARG_NONE, 0, 'X', 0, 0 },
+ {"no-xattrs", 0, POPT_ARG_VAL, &preserve_xattrs, 0, 0, 0 },
+ {"no-X", 0, POPT_ARG_VAL, &preserve_xattrs, 0, 0, 0 },
+ {"times", 't', POPT_ARG_VAL, &preserve_times, 1, 0, 0 },
+ {"no-times", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 },
+ {"no-t", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 },
+ {"omit-dir-times", 'O', POPT_ARG_VAL, &omit_dir_times, 1, 0, 0 },
+ {"no-omit-dir-times",0, POPT_ARG_VAL, &omit_dir_times, 0, 0, 0 },
+ {"no-O", 0, POPT_ARG_VAL, &omit_dir_times, 0, 0, 0 },
+ {"omit-link-times", 'J', POPT_ARG_VAL, &omit_link_times, 1, 0, 0 },
+ {"no-omit-link-times",0, POPT_ARG_VAL, &omit_link_times, 0, 0, 0 },
+ {"no-J", 0, POPT_ARG_VAL, &omit_link_times, 0, 0, 0 },
+ {"modify-window", 0, POPT_ARG_INT, &modify_window, OPT_MODIFY_WINDOW, 0, 0 },
+ {"super", 0, POPT_ARG_VAL, &am_root, 2, 0, 0 },
+ {"no-super", 0, POPT_ARG_VAL, &am_root, 0, 0, 0 },
+ {"fake-super", 0, POPT_ARG_VAL, &am_root, -1, 0, 0 },
+ {"owner", 'o', POPT_ARG_VAL, &preserve_uid, 1, 0, 0 },
+ {"no-owner", 0, POPT_ARG_VAL, &preserve_uid, 0, 0, 0 },
+ {"no-o", 0, POPT_ARG_VAL, &preserve_uid, 0, 0, 0 },
+ {"group", 'g', POPT_ARG_VAL, &preserve_gid, 1, 0, 0 },
+ {"no-group", 0, POPT_ARG_VAL, &preserve_gid, 0, 0, 0 },
+ {"no-g", 0, POPT_ARG_VAL, &preserve_gid, 0, 0, 0 },
+ {0, 'D', POPT_ARG_NONE, 0, 'D', 0, 0 },
+ {"no-D", 0, POPT_ARG_NONE, 0, OPT_NO_D, 0, 0 },
+ {"devices", 0, POPT_ARG_VAL, &preserve_devices, 1, 0, 0 },
+ {"no-devices", 0, POPT_ARG_VAL, &preserve_devices, 0, 0, 0 },
+ {"specials", 0, POPT_ARG_VAL, &preserve_specials, 1, 0, 0 },
+ {"no-specials", 0, POPT_ARG_VAL, &preserve_specials, 0, 0, 0 },
+ {"links", 'l', POPT_ARG_VAL, &preserve_links, 1, 0, 0 },
+ {"no-links", 0, POPT_ARG_VAL, &preserve_links, 0, 0, 0 },
+ {"no-l", 0, POPT_ARG_VAL, &preserve_links, 0, 0, 0 },
+ {"copy-links", 'L', POPT_ARG_NONE, ©_links, 0, 0, 0 },
+ {"copy-unsafe-links",0, POPT_ARG_NONE, ©_unsafe_links, 0, 0, 0 },
+ {"safe-links", 0, POPT_ARG_NONE, &safe_symlinks, 0, 0, 0 },
+ {"munge-links", 0, POPT_ARG_VAL, &munge_symlinks, 1, 0, 0 },
+ {"no-munge-links", 0, POPT_ARG_VAL, &munge_symlinks, 0, 0, 0 },
+ {"copy-dirlinks", 'k', POPT_ARG_NONE, ©_dirlinks, 0, 0, 0 },
+ {"keep-dirlinks", 'K', POPT_ARG_NONE, &keep_dirlinks, 0, 0, 0 },
+ {"hard-links", 'H', POPT_ARG_NONE, 0, 'H', 0, 0 },
+ {"no-hard-links", 0, POPT_ARG_VAL, &preserve_hard_links, 0, 0, 0 },
+ {"no-H", 0, POPT_ARG_VAL, &preserve_hard_links, 0, 0, 0 },
+ {"relative", 'R', POPT_ARG_VAL, &relative_paths, 1, 0, 0 },
+ {"no-relative", 0, POPT_ARG_VAL, &relative_paths, 0, 0, 0 },
+ {"no-R", 0, POPT_ARG_VAL, &relative_paths, 0, 0, 0 },
+ {"implied-dirs", 0, POPT_ARG_VAL, &implied_dirs, 1, 0, 0 },
+ {"no-implied-dirs", 0, POPT_ARG_VAL, &implied_dirs, 0, 0, 0 },
+ {"i-d", 0, POPT_ARG_VAL, &implied_dirs, 1, 0, 0 },
+ {"no-i-d", 0, POPT_ARG_VAL, &implied_dirs, 0, 0, 0 },
+ {"chmod", 0, POPT_ARG_STRING, 0, OPT_CHMOD, 0, 0 },
+ {"ignore-times", 'I', POPT_ARG_NONE, &ignore_times, 0, 0, 0 },
+ {"size-only", 0, POPT_ARG_NONE, &size_only, 0, 0, 0 },
+ {"one-file-system", 'x', POPT_ARG_NONE, 0, 'x', 0, 0 },
+ {"no-one-file-system",0, POPT_ARG_VAL, &one_file_system, 0, 0, 0 },
+ {"no-x", 0, POPT_ARG_VAL, &one_file_system, 0, 0, 0 },
+ {"update", 'u', POPT_ARG_NONE, &update_only, 0, 0, 0 },
+ {"existing", 0, POPT_ARG_NONE, &ignore_non_existing, 0, 0, 0 },
+ {"ignore-non-existing",0,POPT_ARG_NONE, &ignore_non_existing, 0, 0, 0 },
+ {"ignore-existing", 0, POPT_ARG_NONE, &ignore_existing, 0, 0, 0 },
+ {"max-size", 0, POPT_ARG_STRING, &max_size_arg, OPT_MAX_SIZE, 0, 0 },
+ {"min-size", 0, POPT_ARG_STRING, &min_size_arg, OPT_MIN_SIZE, 0, 0 },
+ {"sparse", 'S', POPT_ARG_VAL, &sparse_files, 1, 0, 0 },
+ {"no-sparse", 0, POPT_ARG_VAL, &sparse_files, 0, 0, 0 },
+ {"no-S", 0, POPT_ARG_VAL, &sparse_files, 0, 0, 0 },
+ {"preallocate", 0, POPT_ARG_NONE, &preallocate_files, 0, 0, 0},
+ {"inplace", 0, POPT_ARG_VAL, &inplace, 1, 0, 0 },
+ {"no-inplace", 0, POPT_ARG_VAL, &inplace, 0, 0, 0 },
+ {"append", 0, POPT_ARG_NONE, 0, OPT_APPEND, 0, 0 },
+ {"append-verify", 0, POPT_ARG_VAL, &append_mode, 2, 0, 0 },
+ {"no-append", 0, POPT_ARG_VAL, &append_mode, 0, 0, 0 },
+ {"del", 0, POPT_ARG_NONE, &delete_during, 0, 0, 0 },
+ {"delete", 0, POPT_ARG_NONE, &delete_mode, 0, 0, 0 },
+ {"delete-before", 0, POPT_ARG_NONE, &delete_before, 0, 0, 0 },
+ {"delete-during", 0, POPT_ARG_VAL, &delete_during, 1, 0, 0 },
+ {"delete-delay", 0, POPT_ARG_VAL, &delete_during, 2, 0, 0 },
+ {"delete-after", 0, POPT_ARG_NONE, &delete_after, 0, 0, 0 },
+ {"delete-excluded", 0, POPT_ARG_NONE, &delete_excluded, 0, 0, 0 },
+ {"delete-missing-args",0,POPT_BIT_SET, &missing_args, 2, 0, 0 },
+ {"ignore-missing-args",0,POPT_BIT_SET, &missing_args, 1, 0, 0 },
+ {"remove-sent-files",0, POPT_ARG_VAL, &remove_source_files, 2, 0, 0 }, /* deprecated */
+ {"remove-source-files",0,POPT_ARG_VAL, &remove_source_files, 1, 0, 0 },
+ {"force", 0, POPT_ARG_VAL, &force_delete, 1, 0, 0 },
+ {"no-force", 0, POPT_ARG_VAL, &force_delete, 0, 0, 0 },
+ {"ignore-errors", 0, POPT_ARG_VAL, &ignore_errors, 1, 0, 0 },
+ {"no-ignore-errors", 0, POPT_ARG_VAL, &ignore_errors, 0, 0, 0 },
+ {"max-delete", 0, POPT_ARG_INT, &max_delete, 0, 0, 0 },
+ {0, 'F', POPT_ARG_NONE, 0, 'F', 0, 0 },
+ {"filter", 'f', POPT_ARG_STRING, 0, OPT_FILTER, 0, 0 },
+ {"exclude", 0, POPT_ARG_STRING, 0, OPT_EXCLUDE, 0, 0 },
+ {"include", 0, POPT_ARG_STRING, 0, OPT_INCLUDE, 0, 0 },
+ {"exclude-from", 0, POPT_ARG_STRING, 0, OPT_EXCLUDE_FROM, 0, 0 },
+ {"include-from", 0, POPT_ARG_STRING, 0, OPT_INCLUDE_FROM, 0, 0 },
+ {"cvs-exclude", 'C', POPT_ARG_NONE, &cvs_exclude, 0, 0, 0 },
+ {"whole-file", 'W', POPT_ARG_VAL, &whole_file, 1, 0, 0 },
+ {"no-whole-file", 0, POPT_ARG_VAL, &whole_file, 0, 0, 0 },
+ {"no-W", 0, POPT_ARG_VAL, &whole_file, 0, 0, 0 },
+ {"checksum", 'c', POPT_ARG_VAL, &always_checksum, 1, 0, 0 },
+ {"no-checksum", 0, POPT_ARG_VAL, &always_checksum, 0, 0, 0 },
+ {"no-c", 0, POPT_ARG_VAL, &always_checksum, 0, 0, 0 },
+ {"block-size", 'B', POPT_ARG_LONG, &block_size, 0, 0, 0 },
+ {"compare-dest", 0, POPT_ARG_STRING, 0, OPT_COMPARE_DEST, 0, 0 },
+ {"copy-dest", 0, POPT_ARG_STRING, 0, OPT_COPY_DEST, 0, 0 },
+ {"link-dest", 0, POPT_ARG_STRING, 0, OPT_LINK_DEST, 0, 0 },
+ {"fuzzy", 'y', POPT_ARG_NONE, 0, 'y', 0, 0 },
+ {"no-fuzzy", 0, POPT_ARG_VAL, &fuzzy_basis, 0, 0, 0 },
+ {"no-y", 0, POPT_ARG_VAL, &fuzzy_basis, 0, 0, 0 },
+ {"compress", 'z', POPT_ARG_NONE, 0, 'z', 0, 0 },
+ {"old-compress", 0, POPT_ARG_VAL, &do_compression, 1, 0, 0 },
+ {"new-compress", 0, POPT_ARG_VAL, &do_compression, 2, 0, 0 },
+ {"no-compress", 0, POPT_ARG_VAL, &do_compression, 0, 0, 0 },
+ {"no-z", 0, POPT_ARG_VAL, &do_compression, 0, 0, 0 },
+ {"skip-compress", 0, POPT_ARG_STRING, &skip_compress, 0, 0, 0 },
+ {"compress-level", 0, POPT_ARG_INT, &def_compress_level, 0, 0, 0 },
+ {0, 'P', POPT_ARG_NONE, 0, 'P', 0, 0 },
+ {"progress", 0, POPT_ARG_VAL, &do_progress, 1, 0, 0 },
+ {"no-progress", 0, POPT_ARG_VAL, &do_progress, 0, 0, 0 },
+ {"partial", 0, POPT_ARG_VAL, &keep_partial, 1, 0, 0 },
+ {"no-partial", 0, POPT_ARG_VAL, &keep_partial, 0, 0, 0 },
+ {"partial-dir", 0, POPT_ARG_STRING, &partial_dir, 0, 0, 0 },
+ {"delay-updates", 0, POPT_ARG_VAL, &delay_updates, 1, 0, 0 },
+ {"no-delay-updates", 0, POPT_ARG_VAL, &delay_updates, 0, 0, 0 },
+ {"prune-empty-dirs",'m', POPT_ARG_VAL, &prune_empty_dirs, 1, 0, 0 },
+ {"no-prune-empty-dirs",0,POPT_ARG_VAL, &prune_empty_dirs, 0, 0, 0 },
+ {"no-m", 0, POPT_ARG_VAL, &prune_empty_dirs, 0, 0, 0 },
+ {"log-file", 0, POPT_ARG_STRING, &logfile_name, 0, 0, 0 },
+ {"log-file-format", 0, POPT_ARG_STRING, &logfile_format, 0, 0, 0 },
+ {"out-format", 0, POPT_ARG_STRING, &stdout_format, 0, 0, 0 },
+ {"log-format", 0, POPT_ARG_STRING, &stdout_format, 0, 0, 0 }, /* DEPRECATED */
+ {"itemize-changes", 'i', POPT_ARG_NONE, 0, 'i', 0, 0 },
+ {"no-itemize-changes",0, POPT_ARG_VAL, &itemize_changes, 0, 0, 0 },
+ {"no-i", 0, POPT_ARG_VAL, &itemize_changes, 0, 0, 0 },
+ {"bwlimit", 0, POPT_ARG_STRING, &bwlimit_arg, OPT_BWLIMIT, 0, 0 },
+ {"no-bwlimit", 0, POPT_ARG_VAL, &bwlimit, 0, 0, 0 },
+ {"backup", 'b', POPT_ARG_VAL, &make_backups, 1, 0, 0 },
+ {"no-backup", 0, POPT_ARG_VAL, &make_backups, 0, 0, 0 },
+ {"backup-dir", 0, POPT_ARG_STRING, &backup_dir, 0, 0, 0 },
+ {"suffix", 0, POPT_ARG_STRING, &backup_suffix, 0, 0, 0 },
+ {"list-only", 0, POPT_ARG_VAL, &list_only, 2, 0, 0 },
+ {"read-batch", 0, POPT_ARG_STRING, &batch_name, OPT_READ_BATCH, 0, 0 },
+ {"write-batch", 0, POPT_ARG_STRING, &batch_name, OPT_WRITE_BATCH, 0, 0 },
+ {"only-write-batch", 0, POPT_ARG_STRING, &batch_name, OPT_ONLY_WRITE_BATCH, 0, 0 },
+ {"files-from", 0, POPT_ARG_STRING, &files_from, 0, 0, 0 },
+ {"from0", '0', POPT_ARG_VAL, &eol_nulls, 1, 0, 0},
+ {"no-from0", 0, POPT_ARG_VAL, &eol_nulls, 0, 0, 0},
+ {"protect-args", 's', POPT_ARG_VAL, &protect_args, 1, 0, 0},
+ {"no-protect-args", 0, POPT_ARG_VAL, &protect_args, 0, 0, 0},
+ {"no-s", 0, POPT_ARG_VAL, &protect_args, 0, 0, 0},
+ {"numeric-ids", 0, POPT_ARG_VAL, &numeric_ids, 1, 0, 0 },
+ {"no-numeric-ids", 0, POPT_ARG_VAL, &numeric_ids, 0, 0, 0 },
+ {"usermap", 0, POPT_ARG_STRING, 0, OPT_USERMAP, 0, 0 },
+ {"groupmap", 0, POPT_ARG_STRING, 0, OPT_GROUPMAP, 0, 0 },
+ {"chown", 0, POPT_ARG_STRING, 0, OPT_CHOWN, 0, 0 },
+ {"timeout", 0, POPT_ARG_INT, &io_timeout, 0, 0, 0 },
+ {"no-timeout", 0, POPT_ARG_VAL, &io_timeout, 0, 0, 0 },
+ {"contimeout", 0, POPT_ARG_INT, &connect_timeout, 0, 0, 0 },
+ {"no-contimeout", 0, POPT_ARG_VAL, &connect_timeout, 0, 0, 0 },
+ {"rsh", 'e', POPT_ARG_STRING, &shell_cmd, 0, 0, 0 },
+ {"rsync-path", 0, POPT_ARG_STRING, &rsync_path, 0, 0, 0 },
+ {"temp-dir", 'T', POPT_ARG_STRING, &tmpdir, 0, 0, 0 },
+#ifdef ICONV_OPTION
+ {"iconv", 0, POPT_ARG_STRING, &iconv_opt, 0, 0, 0 },
+ {"no-iconv", 0, POPT_ARG_NONE, 0, OPT_NO_ICONV, 0, 0 },
+#endif
+ {"ipv4", '4', POPT_ARG_VAL, &default_af_hint, AF_INET, 0, 0 },
+ {"ipv6", '6', POPT_ARG_VAL, &default_af_hint, AF_INET6, 0, 0 },
+ {"8-bit-output", '8', POPT_ARG_VAL, &allow_8bit_chars, 1, 0, 0 },
+ {"no-8-bit-output", 0, POPT_ARG_VAL, &allow_8bit_chars, 0, 0, 0 },
+ {"no-8", 0, POPT_ARG_VAL, &allow_8bit_chars, 0, 0, 0 },
+ {"qsort", 0, POPT_ARG_NONE, &use_qsort, 0, 0, 0 },
+ {"address", 0, POPT_ARG_STRING, &bind_address, 0, 0, 0 },
+ {"port", 0, POPT_ARG_INT, &rsync_port, 0, 0, 0 },
+ {"sockopts", 0, POPT_ARG_STRING, &sockopts, 0, 0, 0 },
+ {"password-file", 0, POPT_ARG_STRING, &password_file, 0, 0, 0 },
+ {"blocking-io", 0, POPT_ARG_VAL, &blocking_io, 1, 0, 0 },
+ {"no-blocking-io", 0, POPT_ARG_VAL, &blocking_io, 0, 0, 0 },
+#ifdef HAVE_SETVBUF
+ {"outbuf", 0, POPT_ARG_STRING, &outbuf_mode, 0, 0, 0 },
+#endif
+ {"remote-option", 'M', POPT_ARG_STRING, 0, 'M', 0, 0 },
+ {"protocol", 0, POPT_ARG_INT, &protocol_version, 0, 0, 0 },
+ {"checksum-seed", 0, POPT_ARG_INT, &checksum_seed, 0, 0, 0 },
+ {"server", 0, POPT_ARG_NONE, 0, OPT_SERVER, 0, 0 },
+ {"sender", 0, POPT_ARG_NONE, 0, OPT_SENDER, 0, 0 },
+ /* All the following options switch us into daemon-mode option-parsing. */
+ {"config", 0, POPT_ARG_STRING, 0, OPT_DAEMON, 0, 0 },
+ {"daemon", 0, POPT_ARG_NONE, 0, OPT_DAEMON, 0, 0 },
+ {"dparam", 0, POPT_ARG_STRING, 0, OPT_DAEMON, 0, 0 },
+ {"detach", 0, POPT_ARG_NONE, 0, OPT_DAEMON, 0, 0 },
+ {"no-detach", 0, POPT_ARG_NONE, 0, OPT_DAEMON, 0, 0 },
+ {0,0,0,0, 0, 0, 0}
+};
+
+static void daemon_usage(enum logcode F)
+{
+ print_rsync_version(F);
+
+ rprintf(F,"\n");
+ rprintf(F,"Usage: rsync --daemon [OPTION]...\n");
+ rprintf(F," --address=ADDRESS bind to the specified address\n");
+ rprintf(F," --bwlimit=RATE limit socket I/O bandwidth\n");
+ rprintf(F," --config=FILE specify alternate rsyncd.conf file\n");
+ rprintf(F," -M, --dparam=OVERRIDE override global daemon config parameter\n");
+ rprintf(F," --no-detach do not detach from the parent\n");
+ rprintf(F," --port=PORT listen on alternate port number\n");
+ rprintf(F," --log-file=FILE override the \"log file\" setting\n");
+ rprintf(F," --log-file-format=FMT override the \"log format\" setting\n");
+ rprintf(F," --sockopts=OPTIONS specify custom TCP options\n");
+ rprintf(F," -v, --verbose increase verbosity\n");
+ rprintf(F," -4, --ipv4 prefer IPv4\n");
+ rprintf(F," -6, --ipv6 prefer IPv6\n");
+ rprintf(F," --help show this help screen\n");
+
+ rprintf(F,"\n");
+ rprintf(F,"If you were not trying to invoke rsync as a daemon, avoid using any of the\n");
+ rprintf(F,"daemon-specific rsync options. See also the rsyncd.conf(5) man page.\n");
+}
+
+static struct poptOption long_daemon_options[] = {
+ /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
+ {"address", 0, POPT_ARG_STRING, &bind_address, 0, 0, 0 },
+ {"bwlimit", 0, POPT_ARG_INT, &daemon_bwlimit, 0, 0, 0 },
+ {"config", 0, POPT_ARG_STRING, &config_file, 0, 0, 0 },
+ {"daemon", 0, POPT_ARG_NONE, &daemon_opt, 0, 0, 0 },
+ {"dparam", 'M', POPT_ARG_STRING, 0, 'M', 0, 0 },
+ {"ipv4", '4', POPT_ARG_VAL, &default_af_hint, AF_INET, 0, 0 },
+ {"ipv6", '6', POPT_ARG_VAL, &default_af_hint, AF_INET6, 0, 0 },
+ {"detach", 0, POPT_ARG_VAL, &no_detach, 0, 0, 0 },
+ {"no-detach", 0, POPT_ARG_VAL, &no_detach, 1, 0, 0 },
+ {"log-file", 0, POPT_ARG_STRING, &logfile_name, 0, 0, 0 },
+ {"log-file-format", 0, POPT_ARG_STRING, &logfile_format, 0, 0, 0 },
+ {"port", 0, POPT_ARG_INT, &rsync_port, 0, 0, 0 },
+ {"sockopts", 0, POPT_ARG_STRING, &sockopts, 0, 0, 0 },
+ {"protocol", 0, POPT_ARG_INT, &protocol_version, 0, 0, 0 },
+ {"server", 0, POPT_ARG_NONE, &am_server, 0, 0, 0 },
+ {"temp-dir", 'T', POPT_ARG_STRING, &tmpdir, 0, 0, 0 },
+ {"verbose", 'v', POPT_ARG_NONE, 0, 'v', 0, 0 },
+ {"no-verbose", 0, POPT_ARG_VAL, &verbose, 0, 0, 0 },
+ {"no-v", 0, POPT_ARG_VAL, &verbose, 0, 0, 0 },
+ {"help", 'h', POPT_ARG_NONE, 0, 'h', 0, 0 },
+ {0,0,0,0, 0, 0, 0}
+};
+
+
+static char err_buf[200];
+
+
+/**
+ * Store the option error message, if any, so that we can log the
+ * connection attempt (which requires parsing the options), and then
+ * show the error later on.
+ **/
+void option_error(void)
+{
+ if (!err_buf[0]) {
+ strlcpy(err_buf, "Error parsing options: option may "
+ "be supported on client but not on server?\n",
+ sizeof err_buf);
+ }
+
+ rprintf(FERROR, RSYNC_NAME ": %s", err_buf);
+ msleep(20);
+}
+
+
+/**
+ * Tweak the option table to disable all options that the rsyncd.conf
+ * file has told us to refuse.
+ **/
+static void set_refuse_options(char *bp)
+{
+ struct poptOption *op;
+ char *cp, shortname[2];
+ int is_wild, found_match;
+
+ shortname[1] = '\0';
+
+ while (1) {
+ while (*bp == ' ') bp++;
+ if (!*bp)
+ break;
+ if ((cp = strchr(bp, ' ')) != NULL)
+ *cp= '\0';
+ is_wild = strpbrk(bp, "*?[") != NULL;
+ found_match = 0;
+ for (op = long_options; ; op++) {
+ *shortname = op->shortName;
+ if (!op->longName && !*shortname)
+ break;
+ if ((op->longName && wildmatch(bp, op->longName))
+ || (*shortname && wildmatch(bp, shortname))) {
+ if (op->argInfo == POPT_ARG_VAL)
+ op->argInfo = POPT_ARG_NONE;
+ op->val = (op - long_options) + OPT_REFUSED_BASE;
+ found_match = 1;
+ /* These flags are set to let us easily check
+ * an implied option later in the code. */
+ switch (*shortname) {
+ case 'r': case 'd': case 'l': case 'p':
+ case 't': case 'g': case 'o': case 'D':
+ refused_archive_part = op->val;
+ break;
+ case 'z':
+ refused_compress = op->val;
+ break;
+ case '\0':
+ if (wildmatch("delete", op->longName))
+ refused_delete = op->val;
+ else if (wildmatch("delete-before", op->longName))
+ refused_delete_before = op->val;
+ else if (wildmatch("delete-during", op->longName))
+ refused_delete_during = op->val;
+ else if (wildmatch("partial", op->longName))
+ refused_partial = op->val;
+ else if (wildmatch("progress", op->longName))
+ refused_progress = op->val;
+ else if (wildmatch("inplace", op->longName))
+ refused_inplace = op->val;
+ else if (wildmatch("no-iconv", op->longName))
+ refused_no_iconv = op->val;
+ break;
+ }
+ if (!is_wild)
+ break;
+ }
+ }
+ if (!found_match) {
+ rprintf(FLOG, "No match for refuse-options string \"%s\"\n",
+ bp);
+ }
+ if (!cp)
+ break;
+ *cp = ' ';
+ bp = cp + 1;
+ }
+}
+
+
+static int count_args(const char **argv)
+{
+ int i = 0;
+
+ if (argv) {
+ while (argv[i] != NULL)
+ i++;
+ }
+
+ return i;
+}
+
+
+static OFF_T parse_size_arg(char **size_arg, char def_suf)
+{
+ int reps, mult, make_compatible = 0;
+ const char *arg;
+ OFF_T size = 1;
+
+ for (arg = *size_arg; isDigit(arg); arg++) {}
+ if (*arg == '.')
+ for (arg++; isDigit(arg); arg++) {}
+ switch (*arg && *arg != '+' && *arg != '-' ? *arg++ : def_suf) {
+ case 'b': case 'B':
+ reps = 0;
+ break;
+ case 'k': case 'K':
+ reps = 1;
+ break;
+ case 'm': case 'M':
+ reps = 2;
+ break;
+ case 'g': case 'G':
+ reps = 3;
+ break;
+ default:
+ return -1;
+ }
+ if (*arg == 'b' || *arg == 'B')
+ mult = 1000, make_compatible = 1, arg++;
+ else if (!*arg || *arg == '+' || *arg == '-')
+ mult = 1024;
+ else if (strncasecmp(arg, "ib", 2) == 0)
+ mult = 1024, arg += 2;
+ else
+ return -1;
+ while (reps--)
+ size *= mult;
+ size *= atof(*size_arg);
+ if ((*arg == '+' || *arg == '-') && arg[1] == '1')
+ size += atoi(arg), make_compatible = 1, arg += 2;
+ if (*arg)
+ return -1;
+ if (size > 0 && make_compatible && def_suf == 'b') {
+ /* We convert this manually because we may need %lld precision,
+ * and that's not a portable sprintf() escape. */
+ char buf[128], *s = buf + sizeof buf - 1;
+ OFF_T num = size;
+ *s = '\0';
+ while (num) {
+ *--s = (char)(num % 10) + '0';
+ num /= 10;
+ }
+ if (!(*size_arg = strdup(s)))
+ out_of_memory("parse_size_arg");
+ }
+ return size;
+}
+
+
+static void create_refuse_error(int which)
+{
+ /* The "which" value is the index + OPT_REFUSED_BASE. */
+ struct poptOption *op = &long_options[which - OPT_REFUSED_BASE];
+ int n = snprintf(err_buf, sizeof err_buf,
+ "The server is configured to refuse --%s\n",
+ op->longName) - 1;
+ if (op->shortName) {
+ snprintf(err_buf + n, sizeof err_buf - n,
+ " (-%c)\n", op->shortName);
+ }
+}
+
+
+/**
+ * Process command line arguments. Called on both local and remote.
+ *
+ * @retval 1 if all options are OK; with globals set to appropriate
+ * values
+ *
+ * @retval 0 on error, with err_buf containing an explanation
+ **/
+int parse_arguments(int *argc_p, const char ***argv_p)
+{
+ static poptContext pc;
+ char *ref = lp_refuse_options(module_id);
+ const char *arg, **argv = *argv_p;
+ int argc = *argc_p;
+ int opt;
+
+ if (ref && *ref)
+ set_refuse_options(ref);
+ if (am_daemon) {
+ set_refuse_options("log-file*");
+#ifdef ICONV_OPTION
+ if (!*lp_charset(module_id))
+ set_refuse_options("iconv");
+#endif
+ }
+
+#ifdef ICONV_OPTION
+ if (!am_daemon && protect_args <= 0 && (arg = getenv("RSYNC_ICONV")) != NULL && *arg)
+ iconv_opt = strdup(arg);
+#endif
+
+ /* TODO: Call poptReadDefaultConfig; handle errors. */
+
+ /* The context leaks in case of an error, but if there's a
+ * problem we always exit anyhow. */
+ if (pc)
+ poptFreeContext(pc);
+ pc = poptGetContext(RSYNC_NAME, argc, argv, long_options, 0);
+ if (!am_server)
+ poptReadDefaultConfig(pc, 0);
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ /* most options are handled automatically by popt;
+ * only special cases are returned and listed here. */
+
+ switch (opt) {
+ case OPT_VERSION:
+ print_rsync_version(FINFO);
+ exit_cleanup(0);
+
+ case OPT_SERVER:
+ if (!am_server) {
+ /* Disable popt aliases on the server side and
+ * then start parsing the options again. */
+ poptFreeContext(pc);
+ pc = poptGetContext(RSYNC_NAME, argc, argv,
+ long_options, 0);
+ am_server = 1;
+ }
+#ifdef ICONV_OPTION
+ iconv_opt = NULL;
+#endif
+ break;
+
+ case OPT_SENDER:
+ if (!am_server) {
+ usage(FERROR);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ am_sender = 1;
+ break;
+
+ case OPT_DAEMON:
+ if (am_daemon) {
+ strlcpy(err_buf,
+ "Attempt to hack rsync thwarted!\n",
+ sizeof err_buf);
+ return 0;
+ }
+#ifdef ICONV_OPTION
+ iconv_opt = NULL;
+#endif
+ protect_args = 0;
+ poptFreeContext(pc);
+ pc = poptGetContext(RSYNC_NAME, argc, argv,
+ long_daemon_options, 0);
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ char **cpp;
+ switch (opt) {
+ case 'h':
+ daemon_usage(FINFO);
+ exit_cleanup(0);
+
+ case 'M':
+ arg = poptGetOptArg(pc);
+ if (!strchr(arg, '=')) {
+ rprintf(FERROR,
+ "--dparam value is missing an '=': %s\n",
+ arg);
+ goto daemon_error;
+ }
+ cpp = EXPAND_ITEM_LIST(&dparam_list, char *, 4);
+ *cpp = strdup(arg);
+ break;
+
+ case 'v':
+ verbose++;
+ break;
+
+ default:
+ rprintf(FERROR,
+ "rsync: %s: %s (in daemon mode)\n",
+ poptBadOption(pc, POPT_BADOPTION_NOALIAS),
+ poptStrerror(opt));
+ goto daemon_error;
+ }
+ }
+
+ if (dparam_list.count && !set_dparams(1))
+ exit_cleanup(RERR_SYNTAX);
+
+ if (tmpdir && strlen(tmpdir) >= MAXPATHLEN - 10) {
+ snprintf(err_buf, sizeof err_buf,
+ "the --temp-dir path is WAY too long.\n");
+ return 0;
+ }
+
+ if (!daemon_opt) {
+ rprintf(FERROR, "Daemon option(s) used without --daemon.\n");
+ daemon_error:
+ rprintf(FERROR,
+ "(Type \"rsync --daemon --help\" for assistance with daemon mode.)\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+
+ *argv_p = argv = poptGetArgs(pc);
+ *argc_p = argc = count_args(argv);
+ am_starting_up = 0;
+ daemon_opt = 0;
+ am_daemon = 1;
+ return 1;
+
+ case OPT_MODIFY_WINDOW:
+ /* The value has already been set by popt, but
+ * we need to remember that we're using a
+ * non-default setting. */
+ modify_window_set = 1;
+ break;
+
+ case OPT_FILTER:
+ parse_filter_str(&filter_list, poptGetOptArg(pc),
+ rule_template(0), 0);
+ break;
+
+ case OPT_EXCLUDE:
+ parse_filter_str(&filter_list, poptGetOptArg(pc),
+ rule_template(0), XFLG_OLD_PREFIXES);
+ break;
+
+ case OPT_INCLUDE:
+ parse_filter_str(&filter_list, poptGetOptArg(pc),
+ rule_template(FILTRULE_INCLUDE), XFLG_OLD_PREFIXES);
+ break;
+
+ case OPT_EXCLUDE_FROM:
+ case OPT_INCLUDE_FROM:
+ arg = poptGetOptArg(pc);
+ if (sanitize_paths)
+ arg = sanitize_path(NULL, arg, NULL, 0, SP_DEFAULT);
+ if (daemon_filter_list.head) {
+ int rej;
+ char *cp = strdup(arg);
+ if (!cp)
+ out_of_memory("parse_arguments");
+ if (!*cp)
+ rej = 1;
+ else {
+ char *dir = cp + (*cp == '/' ? module_dirlen : 0);
+ clean_fname(dir, CFN_COLLAPSE_DOT_DOT_DIRS);
+ rej = check_filter(&daemon_filter_list, FLOG, dir, 0) < 0;
+ }
+ free(cp);
+ if (rej)
+ goto options_rejected;
+ }
+ parse_filter_file(&filter_list, arg,
+ rule_template(opt == OPT_INCLUDE_FROM ? FILTRULE_INCLUDE : 0),
+ XFLG_FATAL_ERRORS | XFLG_OLD_PREFIXES);
+ break;
+
+ case 'a':
+ if (refused_archive_part) {
+ create_refuse_error(refused_archive_part);
+ return 0;
+ }
+ if (!recurse) /* preserve recurse == 2 */
+ recurse = 1;
+#ifdef SUPPORT_LINKS
+ preserve_links = 1;
+#endif
+ preserve_perms = 1;
+ preserve_times = 1;
+ preserve_gid = 1;
+ preserve_uid = 1;
+ preserve_devices = 1;
+ preserve_specials = 1;
+ break;
+
+ case 'D':
+ preserve_devices = preserve_specials = 1;
+ break;
+
+ case OPT_NO_D:
+ preserve_devices = preserve_specials = 0;
+ break;
+
+ case 'h':
+ human_readable++;
+ break;
+
+ case 'H':
+ preserve_hard_links++;
+ break;
+
+ case 'i':
+ itemize_changes++;
+ break;
+
+ case 'v':
+ verbose++;
+ break;
+
+ case 'y':
+ fuzzy_basis++;
+ break;
+
+ case 'q':
+ quiet++;
+ break;
+
+ case 'x':
+ one_file_system++;
+ break;
+
+ case 'F':
+ switch (++F_option_cnt) {
+ case 1:
+ parse_filter_str(&filter_list,": /.rsync-filter",rule_template(0),0);
+ break;
+ case 2:
+ parse_filter_str(&filter_list,"- .rsync-filter",rule_template(0),0);
+ break;
+ }
+ break;
+
+ case 'P':
+ if (refused_partial || refused_progress) {
+ create_refuse_error(refused_partial
+ ? refused_partial : refused_progress);
+ return 0;
+ }
+ do_progress = 1;
+ keep_partial = 1;
+ break;
+
+ case 'z':
+ do_compression++;
+ break;
+
+ case 'M':
+ arg = poptGetOptArg(pc);
+ if (*arg != '-') {
+ snprintf(err_buf, sizeof err_buf,
+ "Remote option must start with a dash: %s\n", arg);
+ return 0;
+ }
+ if (remote_option_cnt+2 >= remote_option_alloc) {
+ remote_option_alloc += 16;
+ remote_options = realloc_array(remote_options,
+ const char *, remote_option_alloc);
+ if (!remote_options)
+ out_of_memory("parse_arguments");
+ if (!remote_option_cnt)
+ remote_options[0] = "ARG0";
+ }
+ remote_options[++remote_option_cnt] = arg;
+ remote_options[remote_option_cnt+1] = NULL;
+ break;
+
+ case OPT_WRITE_BATCH:
+ /* batch_name is already set */
+ write_batch = 1;
+ break;
+
+ case OPT_ONLY_WRITE_BATCH:
+ /* batch_name is already set */
+ write_batch = -1;
+ break;
+
+ case OPT_READ_BATCH:
+ /* batch_name is already set */
+ read_batch = 1;
+ break;
+
+ case OPT_NO_ICONV:
+#ifdef ICONV_OPTION
+ iconv_opt = NULL;
+#endif
+ break;
+
+ case OPT_MAX_SIZE:
+ if ((max_size = parse_size_arg(&max_size_arg, 'b')) < 0) {
+ snprintf(err_buf, sizeof err_buf,
+ "--max-size value is invalid: %s\n",
+ max_size_arg);
+ return 0;
+ }
+ break;
+
+ case OPT_MIN_SIZE:
+ if ((min_size = parse_size_arg(&min_size_arg, 'b')) < 0) {
+ snprintf(err_buf, sizeof err_buf,
+ "--min-size value is invalid: %s\n",
+ min_size_arg);
+ return 0;
+ }
+ break;
+
+ case OPT_BWLIMIT:
+ {
+ OFF_T limit = parse_size_arg(&bwlimit_arg, 'K');
+ if (limit < 0) {
+ snprintf(err_buf, sizeof err_buf,
+ "--bwlimit value is invalid: %s\n", bwlimit_arg);
+ return 0;
+ }
+ bwlimit = (limit + 512) / 1024;
+ if (limit && !bwlimit) {
+ snprintf(err_buf, sizeof err_buf,
+ "--bwlimit value is too small: %s\n", bwlimit_arg);
+ return 0;
+ }
+ }
+ break;
+
+ case OPT_APPEND:
+ if (am_server)
+ append_mode++;
+ else
+ append_mode = 1;
+ break;
+
+ case OPT_LINK_DEST:
+#ifdef SUPPORT_HARD_LINKS
+ link_dest = 1;
+ dest_option = "--link-dest";
+ goto set_dest_dir;
+#else
+ snprintf(err_buf, sizeof err_buf,
+ "hard links are not supported on this %s\n",
+ am_server ? "server" : "client");
+ return 0;
+#endif
+
+ case OPT_COPY_DEST:
+ copy_dest = 1;
+ dest_option = "--copy-dest";
+ goto set_dest_dir;
+
+ case OPT_COMPARE_DEST:
+ compare_dest = 1;
+ dest_option = "--compare-dest";
+ set_dest_dir:
+ if (basis_dir_cnt >= MAX_BASIS_DIRS) {
+ snprintf(err_buf, sizeof err_buf,
+ "ERROR: at most %d %s args may be specified\n",
+ MAX_BASIS_DIRS, dest_option);
+ return 0;
+ }
+ /* We defer sanitizing this arg until we know what
+ * our destination directory is going to be. */
+ basis_dir[basis_dir_cnt++] = (char *)poptGetOptArg(pc);
+ break;
+
+ case OPT_CHMOD:
+ arg = poptGetOptArg(pc);
+ if (!parse_chmod(arg, &chmod_modes)) {
+ snprintf(err_buf, sizeof err_buf,
+ "Invalid argument passed to --chmod (%s)\n",
+ arg);
+ return 0;
+ }
+ break;
+
+ case OPT_INFO:
+ arg = poptGetOptArg(pc);
+ parse_output_words(info_words, info_levels, arg, USER_PRIORITY);
+ break;
+
+ case OPT_DEBUG:
+ arg = poptGetOptArg(pc);
+ parse_output_words(debug_words, debug_levels, arg, USER_PRIORITY);
+ break;
+
+ case OPT_USERMAP:
+ if (usermap) {
+ if (usermap_via_chown) {
+ snprintf(err_buf, sizeof err_buf,
+ "--usermap conflicts with prior --chown.\n");
+ return 0;
+ }
+ snprintf(err_buf, sizeof err_buf,
+ "You can only specify --usermap once.\n");
+ return 0;
+ }
+ usermap = (char *)poptGetOptArg(pc);
+ usermap_via_chown = False;
+ break;
+
+ case OPT_GROUPMAP:
+ if (groupmap) {
+ if (groupmap_via_chown) {
+ snprintf(err_buf, sizeof err_buf,
+ "--groupmap conflicts with prior --chown.\n");
+ return 0;
+ }
+ snprintf(err_buf, sizeof err_buf,
+ "You can only specify --groupmap once.\n");
+ return 0;
+ }
+ groupmap = (char *)poptGetOptArg(pc);
+ groupmap_via_chown = False;
+ break;
+
+ case OPT_CHOWN: {
+ const char *chown = poptGetOptArg(pc);
+ int len;
+ if ((arg = strchr(chown, ':')) != NULL)
+ len = arg++ - chown;
+ else
+ len = strlen(chown);
+ if (len) {
+ if (usermap) {
+ if (!usermap_via_chown) {
+ snprintf(err_buf, sizeof err_buf,
+ "--chown conflicts with prior --usermap.\n");
+ return 0;
+ }
+ snprintf(err_buf, sizeof err_buf,
+ "You can only specify a user-affecting --chown once.\n");
+ return 0;
+ }
+ if (asprintf(&usermap, "*:%.*s", len, chown) < 0)
+ out_of_memory("parse_arguments");
+ usermap_via_chown = True;
+ }
+ if (arg && *arg) {
+ if (groupmap) {
+ if (!groupmap_via_chown) {
+ snprintf(err_buf, sizeof err_buf,
+ "--chown conflicts with prior --groupmap.\n");
+ return 0;
+ }
+ snprintf(err_buf, sizeof err_buf,
+ "You can only specify a group-affecting --chown once.\n");
+ return 0;
+ }
+ if (asprintf(&groupmap, "*:%s", arg) < 0)
+ out_of_memory("parse_arguments");
+ groupmap_via_chown = True;
+ }
+ break;
+ }
+
+ case OPT_HELP:
+ usage(FINFO);
+ exit_cleanup(0);
+
+ case 'A':
+#ifdef SUPPORT_ACLS
+ preserve_acls = 1;
+ preserve_perms = 1;
+ break;
+#else
+ /* FIXME: this should probably be ignored with a
+ * warning and then countermeasures taken to
+ * restrict group and other access in the presence
+ * of any more restrictive ACLs, but this is safe
+ * for now */
+ snprintf(err_buf,sizeof(err_buf),
+ "ACLs are not supported on this %s\n",
+ am_server ? "server" : "client");
+ return 0;
+#endif
+
+ case 'X':
+#ifdef SUPPORT_XATTRS
+ preserve_xattrs++;
+ break;
+#else
+ snprintf(err_buf,sizeof(err_buf),
+ "extended attributes are not supported on this %s\n",
+ am_server ? "server" : "client");
+ return 0;
+#endif
+
+ default:
+ /* A large opt value means that set_refuse_options()
+ * turned this option off. */
+ if (opt >= OPT_REFUSED_BASE) {
+ create_refuse_error(opt);
+ return 0;
+ }
+ snprintf(err_buf, sizeof err_buf, "%s%s: %s\n",
+ am_server ? "on remote machine: " : "",
+ poptBadOption(pc, POPT_BADOPTION_NOALIAS),
+ poptStrerror(opt));
+ return 0;
+ }
+ }
+
+ if (protect_args < 0) {
+ if (am_server)
+ protect_args = 0;
+ else if ((arg = getenv("RSYNC_PROTECT_ARGS")) != NULL && *arg)
+ protect_args = atoi(arg) ? 1 : 0;
+ else {
+#ifdef RSYNC_USE_PROTECTED_ARGS
+ protect_args = 1;
+#else
+ protect_args = 0;
+#endif
+ }
+ }
+
+ if (human_readable > 1 && argc == 2 && !am_server) {
+ /* Allow the old meaning of 'h' (--help) on its own. */
+ usage(FINFO);
+ exit_cleanup(0);
+ }
+
+ if (do_compression || def_compress_level != NOT_SPECIFIED) {
+ if (def_compress_level == NOT_SPECIFIED)
+ def_compress_level = Z_DEFAULT_COMPRESSION;
+ else if (def_compress_level < Z_DEFAULT_COMPRESSION || def_compress_level > Z_BEST_COMPRESSION) {
+ snprintf(err_buf, sizeof err_buf, "--compress-level value is invalid: %d\n",
+ def_compress_level);
+ return 0;
+ } else if (def_compress_level == Z_NO_COMPRESSION)
+ do_compression = 0;
+ else if (!do_compression)
+ do_compression = 1;
+ if (do_compression && refused_compress) {
+ create_refuse_error(refused_compress);
+ return 0;
+ }
+#ifdef EXTERNAL_ZLIB
+ if (do_compression == 1) {
+ snprintf(err_buf, sizeof err_buf,
+ "This rsync lacks old-style --compress due to its external zlib. Try -zz.\n");
+ if (am_server)
+ return 0;
+ fprintf(stderr, "%s" "Continuing without compression.\n\n", err_buf);
+ do_compression = 0;
+ }
+#endif
+ }
+
+#ifdef HAVE_SETVBUF
+ if (outbuf_mode && !am_server) {
+ int mode = *(uchar *)outbuf_mode;
+ if (islower(mode))
+ mode = toupper(mode);
+ fflush(stdout); /* Just in case... */
+ switch (mode) {
+ case 'N': /* None */
+ case 'U': /* Unbuffered */
+ mode = _IONBF;
+ break;
+ case 'L': /* Line */
+ mode = _IOLBF;
+ break;
+ case 'B': /* Block */
+ case 'F': /* Full */
+ mode = _IOFBF;
+ break;
+ default:
+ snprintf(err_buf, sizeof err_buf,
+ "Invalid --outbuf setting -- specify N, L, or B.\n");
+ return 0;
+ }
+ setvbuf(stdout, (char *)NULL, mode, 0);
+ }
+
+ if (msgs2stderr) {
+ /* Make stderr line buffered for better sharing of the stream. */
+ fflush(stderr); /* Just in case... */
+ setvbuf(stderr, (char *)NULL, _IOLBF, 0);
+ }
+#endif
+
+ set_output_verbosity(verbose, DEFAULT_PRIORITY);
+
+ if (do_stats) {
+ parse_output_words(info_words, info_levels,
+ verbose > 1 ? "stats3" : "stats2", DEFAULT_PRIORITY);
+ }
+
+#ifdef ICONV_OPTION
+ if (iconv_opt && protect_args != 2) {
+ if (!am_server && strcmp(iconv_opt, "-") == 0)
+ iconv_opt = NULL;
+ else
+ need_unsorted_flist = 1;
+ }
+ if (refused_no_iconv && !iconv_opt) {
+ create_refuse_error(refused_no_iconv);
+ return 0;
+ }
+#endif
+
+ if (fuzzy_basis > 1)
+ fuzzy_basis = basis_dir_cnt + 1;
+
+ if (protect_args == 1 && am_server)
+ return 1;
+
+ *argv_p = argv = poptGetArgs(pc);
+ *argc_p = argc = count_args(argv);
+
+#ifndef SUPPORT_LINKS
+ if (preserve_links && !am_sender) {
+ snprintf(err_buf, sizeof err_buf,
+ "symlinks are not supported on this %s\n",
+ am_server ? "server" : "client");
+ return 0;
+ }
+#endif
+
+#ifndef SUPPORT_HARD_LINKS
+ if (preserve_hard_links) {
+ snprintf(err_buf, sizeof err_buf,
+ "hard links are not supported on this %s\n",
+ am_server ? "server" : "client");
+ return 0;
+ }
+#endif
+
+#ifdef SUPPORT_XATTRS
+ if (am_root < 0 && preserve_xattrs > 1) {
+ snprintf(err_buf, sizeof err_buf,
+ "--fake-super conflicts with -XX\n");
+ return 0;
+ }
+#else
+ if (am_root < 0) {
+ snprintf(err_buf, sizeof err_buf,
+ "--fake-super requires an rsync with extended attributes enabled\n");
+ return 0;
+ }
+#endif
+
+ if (block_size > MAX_BLOCK_SIZE) {
+ snprintf(err_buf, sizeof err_buf,
+ "--block-size=%lu is too large (max: %u)\n", block_size, MAX_BLOCK_SIZE);
+ return 0;
+ }
+
+ if (write_batch && read_batch) {
+ snprintf(err_buf, sizeof err_buf,
+ "--write-batch and --read-batch can not be used together\n");
+ return 0;
+ }
+ if (write_batch > 0 || read_batch) {
+ if (am_server) {
+ rprintf(FINFO,
+ "ignoring --%s-batch option sent to server\n",
+ write_batch ? "write" : "read");
+ /* We don't actually exit_cleanup(), so that we can
+ * still service older version clients that still send
+ * batch args to server. */
+ read_batch = write_batch = 0;
+ batch_name = NULL;
+ } else if (dry_run)
+ write_batch = 0;
+ } else if (write_batch < 0 && dry_run)
+ write_batch = 0;
+ if (read_batch && files_from) {
+ snprintf(err_buf, sizeof err_buf,
+ "--read-batch cannot be used with --files-from\n");
+ return 0;
+ }
+ if (read_batch && remove_source_files) {
+ snprintf(err_buf, sizeof err_buf,
+ "--read-batch cannot be used with --remove-%s-files\n",
+ remove_source_files == 1 ? "source" : "sent");
+ return 0;
+ }
+ if (batch_name && strlen(batch_name) > MAX_BATCH_NAME_LEN) {
+ snprintf(err_buf, sizeof err_buf,
+ "the batch-file name must be %d characters or less.\n",
+ MAX_BATCH_NAME_LEN);
+ return 0;
+ }
+
+ if (tmpdir && strlen(tmpdir) >= MAXPATHLEN - 10) {
+ snprintf(err_buf, sizeof err_buf,
+ "the --temp-dir path is WAY too long.\n");
+ return 0;
+ }
+
+ if (max_delete < 0 && max_delete != INT_MIN) {
+ /* Negative numbers are treated as "no deletions". */
+ max_delete = 0;
+ }
+
+ if (compare_dest + copy_dest + link_dest > 1) {
+ snprintf(err_buf, sizeof err_buf,
+ "You may not mix --compare-dest, --copy-dest, and --link-dest.\n");
+ return 0;
+ }
+
+ if (files_from) {
+ if (recurse == 1) /* preserve recurse == 2 */
+ recurse = 0;
+ if (xfer_dirs < 0)
+ xfer_dirs = 1;
+ }
+
+ if (argc < 2 && !read_batch && !am_server)
+ list_only |= 1;
+
+ if (xfer_dirs >= 4) {
+ parse_filter_str(&filter_list, "- /*/*", rule_template(0), 0);
+ recurse = xfer_dirs = 1;
+ } else if (recurse)
+ xfer_dirs = 1;
+ else if (xfer_dirs < 0)
+ xfer_dirs = list_only ? 1 : 0;
+
+ if (relative_paths < 0)
+ relative_paths = files_from? 1 : 0;
+ if (!relative_paths)
+ implied_dirs = 0;
+
+ if (delete_before + !!delete_during + delete_after > 1) {
+ snprintf(err_buf, sizeof err_buf,
+ "You may not combine multiple --delete-WHEN options.\n");
+ return 0;
+ }
+ if (delete_before || delete_during || delete_after)
+ delete_mode = 1;
+ else if (delete_mode || delete_excluded) {
+ /* Only choose now between before & during if one is refused. */
+ if (refused_delete_before) {
+ if (!refused_delete_during)
+ delete_during = 1;
+ else {
+ create_refuse_error(refused_delete_before);
+ return 0;
+ }
+ } else if (refused_delete_during)
+ delete_before = 1;
+ delete_mode = 1;
+ }
+ if (!xfer_dirs && delete_mode) {
+ snprintf(err_buf, sizeof err_buf,
+ "--delete does not work without --recursive (-r) or --dirs (-d).\n");
+ return 0;
+ }
+
+ if (missing_args == 3) /* simplify if both options were specified */
+ missing_args = 2;
+ if (refused_delete && (delete_mode || missing_args == 2)) {
+ create_refuse_error(refused_delete);
+ return 0;
+ }
+
+ if (remove_source_files) {
+ /* We only want to infer this refusal of --remove-source-files
+ * via the refusal of "delete", not any of the "delete-FOO"
+ * options. */
+ if (refused_delete && am_sender) {
+ create_refuse_error(refused_delete);
+ return 0;
+ }
+ need_messages_from_generator = 1;
+ }
+
+ if (munge_symlinks && !am_daemon) {
+ STRUCT_STAT st;
+ char prefix[SYMLINK_PREFIX_LEN]; /* NOT +1 ! */
+ strlcpy(prefix, SYMLINK_PREFIX, sizeof prefix); /* trim the trailing slash */
+ if (do_stat(prefix, &st) == 0 && S_ISDIR(st.st_mode)) {
+ rprintf(FERROR, "Symlink munging is unsafe when a %s directory exists.\n",
+ prefix);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+ }
+
+ if (sanitize_paths) {
+ int i;
+ for (i = argc; i-- > 0; )
+ argv[i] = sanitize_path(NULL, argv[i], "", 0, SP_KEEP_DOT_DIRS);
+ if (tmpdir)
+ tmpdir = sanitize_path(NULL, tmpdir, NULL, 0, SP_DEFAULT);
+ if (backup_dir)
+ backup_dir = sanitize_path(NULL, backup_dir, NULL, 0, SP_DEFAULT);
+ }
+ if (daemon_filter_list.head && !am_sender) {
+ filter_rule_list *elp = &daemon_filter_list;
+ if (tmpdir) {
+ char *dir;
+ if (!*tmpdir)
+ goto options_rejected;
+ dir = tmpdir + (*tmpdir == '/' ? module_dirlen : 0);
+ clean_fname(dir, CFN_COLLAPSE_DOT_DOT_DIRS);
+ if (check_filter(elp, FLOG, dir, 1) < 0)
+ goto options_rejected;
+ }
+ if (backup_dir) {
+ char *dir;
+ if (!*backup_dir)
+ goto options_rejected;
+ dir = backup_dir + (*backup_dir == '/' ? module_dirlen : 0);
+ clean_fname(dir, CFN_COLLAPSE_DOT_DOT_DIRS);
+ if (check_filter(elp, FLOG, dir, 1) < 0)
+ goto options_rejected;
+ }
+ }
+
+ if (!backup_suffix)
+ backup_suffix = backup_dir ? "" : BACKUP_SUFFIX;
+ backup_suffix_len = strlen(backup_suffix);
+ if (strchr(backup_suffix, '/') != NULL) {
+ snprintf(err_buf, sizeof err_buf,
+ "--suffix cannot contain slashes: %s\n",
+ backup_suffix);
+ return 0;
+ }
+ if (backup_dir) {
+ size_t len;
+ while (*backup_dir == '.' && backup_dir[1] == '/')
+ backup_dir += 2;
+ if (*backup_dir == '.' && backup_dir[1] == '\0')
+ backup_dir++;
+ len = strlcpy(backup_dir_buf, backup_dir, sizeof backup_dir_buf);
+ if (len > sizeof backup_dir_buf - 128) {
+ snprintf(err_buf, sizeof err_buf,
+ "the --backup-dir path is WAY too long.\n");
+ return 0;
+ }
+ backup_dir_len = (int)len;
+ if (!backup_dir_len) {
+ backup_dir_len = -1;
+ backup_dir = NULL;
+ } else if (backup_dir_buf[backup_dir_len - 1] != '/') {
+ backup_dir_buf[backup_dir_len++] = '/';
+ backup_dir_buf[backup_dir_len] = '\0';
+ }
+ backup_dir_remainder = sizeof backup_dir_buf - backup_dir_len;
+ }
+ if (backup_dir) {
+ /* No need for a suffix or a protect rule. */
+ } else if (!backup_suffix_len && (!am_server || !am_sender)) {
+ snprintf(err_buf, sizeof err_buf,
+ "--suffix cannot be empty %s\n", backup_dir_len < 0
+ ? "when --backup-dir is the same as the dest dir"
+ : "without a --backup-dir");
+ return 0;
+ } else if (make_backups && delete_mode && !delete_excluded && !am_server) {
+ snprintf(backup_dir_buf, sizeof backup_dir_buf,
+ "P *%s", backup_suffix);
+ parse_filter_str(&filter_list, backup_dir_buf, rule_template(0), 0);
+ }
+
+ if (preserve_times) {
+ preserve_times = PRESERVE_FILE_TIMES;
+ if (!omit_dir_times)
+ preserve_times |= PRESERVE_DIR_TIMES;
+#ifdef CAN_SET_SYMLINK_TIMES
+ if (!omit_link_times)
+ preserve_times |= PRESERVE_LINK_TIMES;
+#endif
+ }
+
+ if (make_backups && !backup_dir) {
+ omit_dir_times = 0; /* Implied, so avoid -O to sender. */
+ preserve_times &= ~PRESERVE_DIR_TIMES;
+ }
+
+ if (stdout_format) {
+ if (am_server && log_format_has(stdout_format, 'I'))
+ stdout_format_has_i = 2;
+ else if (log_format_has(stdout_format, 'i'))
+ stdout_format_has_i = itemize_changes | 1;
+ if (!log_format_has(stdout_format, 'b')
+ && !log_format_has(stdout_format, 'c')
+ && !log_format_has(stdout_format, 'C'))
+ log_before_transfer = !am_server;
+ } else if (itemize_changes) {
+ stdout_format = "%i %n%L";
+ stdout_format_has_i = itemize_changes;
+ log_before_transfer = !am_server;
+ }
+
+ if (do_progress && !am_server) {
+ if (!log_before_transfer && INFO_EQ(NAME, 0))
+ parse_output_words(info_words, info_levels, "name", DEFAULT_PRIORITY);
+ parse_output_words(info_words, info_levels, "flist2,progress", DEFAULT_PRIORITY);
+ }
+
+ if (dry_run)
+ do_xfers = 0;
+
+ set_io_timeout(io_timeout);
+
+ if (INFO_GTE(NAME, 1) && !stdout_format) {
+ stdout_format = "%n%L";
+ log_before_transfer = !am_server;
+ }
+ if (stdout_format_has_i || log_format_has(stdout_format, 'o'))
+ stdout_format_has_o_or_i = 1;
+
+ if (logfile_name && !am_daemon) {
+ if (!logfile_format) {
+ logfile_format = "%i %n%L";
+ logfile_format_has_i = logfile_format_has_o_or_i = 1;
+ } else {
+ if (log_format_has(logfile_format, 'i'))
+ logfile_format_has_i = 1;
+ if (logfile_format_has_i || log_format_has(logfile_format, 'o'))
+ logfile_format_has_o_or_i = 1;
+ }
+ log_init(0);
+ } else if (!am_daemon)
+ logfile_format = NULL;
+
+ if (daemon_bwlimit && (!bwlimit || bwlimit > daemon_bwlimit))
+ bwlimit = daemon_bwlimit;
+ if (bwlimit) {
+ bwlimit_writemax = (size_t)bwlimit * 128;
+ if (bwlimit_writemax < 512)
+ bwlimit_writemax = 512;
+ }
+
+ if (sparse_files && inplace) {
+ /* Note: we don't check for this below, because --append is
+ * OK with --sparse (as long as redos are handled right). */
+ snprintf(err_buf, sizeof err_buf,
+ "--sparse cannot be used with --inplace\n");
+ return 0;
+ }
+
+ if (append_mode) {
+ if (whole_file > 0) {
+ snprintf(err_buf, sizeof err_buf,
+ "--append cannot be used with --whole-file\n");
+ return 0;
+ }
+ if (refused_inplace) {
+ create_refuse_error(refused_inplace);
+ return 0;
+ }
+ inplace = 1;
+ }
+
+ if (delay_updates && !partial_dir)
+ partial_dir = tmp_partialdir;
+
+ if (inplace) {
+#ifdef HAVE_FTRUNCATE
+ if (partial_dir) {
+ snprintf(err_buf, sizeof err_buf,
+ "--%s cannot be used with --%s\n",
+ append_mode ? "append" : "inplace",
+ delay_updates ? "delay-updates" : "partial-dir");
+ return 0;
+ }
+ /* --inplace implies --partial for refusal purposes, but we
+ * clear the keep_partial flag for internal logic purposes. */
+ if (refused_partial) {
+ create_refuse_error(refused_partial);
+ return 0;
+ }
+ keep_partial = 0;
+#else
+ snprintf(err_buf, sizeof err_buf,
+ "--%s is not supported on this %s\n",
+ append_mode ? "append" : "inplace",
+ am_server ? "server" : "client");
+ return 0;
+#endif
+ } else {
+ if (keep_partial && !partial_dir && !am_server) {
+ if ((arg = getenv("RSYNC_PARTIAL_DIR")) != NULL && *arg)
+ partial_dir = strdup(arg);
+ }
+ if (partial_dir) {
+ if (*partial_dir)
+ clean_fname(partial_dir, CFN_COLLAPSE_DOT_DOT_DIRS);
+ if (!*partial_dir || strcmp(partial_dir, ".") == 0)
+ partial_dir = NULL;
+ if (!partial_dir && refused_partial) {
+ create_refuse_error(refused_partial);
+ return 0;
+ }
+ keep_partial = 1;
+ }
+ }
+
+ if (files_from) {
+ char *h, *p;
+ int q;
+ if (argc > 2 || (!am_daemon && !am_server && argc == 1)) {
+ usage(FERROR);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (strcmp(files_from, "-") == 0) {
+ filesfrom_fd = 0;
+ if (am_server)
+ filesfrom_host = ""; /* reading from socket */
+ } else if ((p = check_for_hostspec(files_from, &h, &q)) != 0) {
+ if (am_server) {
+ snprintf(err_buf, sizeof err_buf,
+ "The --files-from sent to the server cannot specify a host.\n");
+ return 0;
+ }
+ files_from = p;
+ filesfrom_host = h;
+ if (strcmp(files_from, "-") == 0) {
+ snprintf(err_buf, sizeof err_buf,
+ "Invalid --files-from remote filename\n");
+ return 0;
+ }
+ } else {
+ if (sanitize_paths)
+ files_from = sanitize_path(NULL, files_from, NULL, 0, SP_DEFAULT);
+ if (daemon_filter_list.head) {
+ char *dir;
+ if (!*files_from)
+ goto options_rejected;
+ dir = files_from + (*files_from == '/' ? module_dirlen : 0);
+ clean_fname(dir, CFN_COLLAPSE_DOT_DOT_DIRS);
+ if (check_filter(&daemon_filter_list, FLOG, dir, 0) < 0)
+ goto options_rejected;
+ }
+ filesfrom_fd = open(files_from, O_RDONLY|O_BINARY);
+ if (filesfrom_fd < 0) {
+ snprintf(err_buf, sizeof err_buf,
+ "failed to open files-from file %s: %s\n",
+ files_from, strerror(errno));
+ return 0;
+ }
+ }
+ }
+
+ am_starting_up = 0;
+
+ return 1;
+
+ options_rejected:
+ snprintf(err_buf, sizeof err_buf,
+ "Your options have been rejected by the server.\n");
+ return 0;
+}
+
+
+/**
+ * Construct a filtered list of options to pass through from the
+ * client to the server.
+ *
+ * This involves setting options that will tell the server how to
+ * behave, and also filtering out options that are processed only
+ * locally.
+ **/
+void server_options(char **args, int *argc_p)
+{
+ static char argstr[64];
+ int ac = *argc_p;
+ uchar where;
+ char *arg;
+ int i, x;
+
+ /* This should always remain first on the server's command-line. */
+ args[ac++] = "--server";
+
+ if (daemon_over_rsh > 0) {
+ args[ac++] = "--daemon";
+ *argc_p = ac;
+ /* if we're passing --daemon, we're done */
+ return;
+ }
+
+ if (!am_sender)
+ args[ac++] = "--sender";
+
+ x = 1;
+ argstr[0] = '-';
+
+ if (protect_args)
+ argstr[x++] = 's';
+
+ for (i = 0; i < verbose; i++)
+ argstr[x++] = 'v';
+
+ /* the -q option is intentionally left out */
+ if (make_backups)
+ argstr[x++] = 'b';
+ if (update_only)
+ argstr[x++] = 'u';
+ if (!do_xfers) /* Note: NOT "dry_run"! */
+ argstr[x++] = 'n';
+ if (preserve_links)
+ argstr[x++] = 'l';
+ if ((xfer_dirs >= 2 && xfer_dirs < 4)
+ || (xfer_dirs && !recurse && (list_only || (delete_mode && am_sender))))
+ argstr[x++] = 'd';
+ if (am_sender) {
+ if (keep_dirlinks)
+ argstr[x++] = 'K';
+ if (prune_empty_dirs)
+ argstr[x++] = 'm';
+ if (omit_dir_times)
+ argstr[x++] = 'O';
+ if (omit_link_times)
+ argstr[x++] = 'J';
+ if (fuzzy_basis) {
+ argstr[x++] = 'y';
+ if (fuzzy_basis > 1)
+ argstr[x++] = 'y';
+ }
+ } else {
+ if (copy_links)
+ argstr[x++] = 'L';
+ if (copy_dirlinks)
+ argstr[x++] = 'k';
+ }
+
+ if (whole_file > 0)
+ argstr[x++] = 'W';
+ /* We don't need to send --no-whole-file, because it's the
+ * default for remote transfers, and in any case old versions
+ * of rsync will not understand it. */
+
+ if (preserve_hard_links) {
+ argstr[x++] = 'H';
+ if (preserve_hard_links > 1)
+ argstr[x++] = 'H';
+ }
+ if (preserve_uid)
+ argstr[x++] = 'o';
+ if (preserve_gid)
+ argstr[x++] = 'g';
+ if (preserve_devices) /* ignore preserve_specials here */
+ argstr[x++] = 'D';
+ if (preserve_times)
+ argstr[x++] = 't';
+ if (preserve_perms)
+ argstr[x++] = 'p';
+ else if (preserve_executability && am_sender)
+ argstr[x++] = 'E';
+#ifdef SUPPORT_ACLS
+ if (preserve_acls)
+ argstr[x++] = 'A';
+#endif
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs) {
+ argstr[x++] = 'X';
+ if (preserve_xattrs > 1)
+ argstr[x++] = 'X';
+ }
+#endif
+ if (recurse)
+ argstr[x++] = 'r';
+ if (always_checksum)
+ argstr[x++] = 'c';
+ if (cvs_exclude)
+ argstr[x++] = 'C';
+ if (ignore_times)
+ argstr[x++] = 'I';
+ if (relative_paths)
+ argstr[x++] = 'R';
+ if (one_file_system) {
+ argstr[x++] = 'x';
+ if (one_file_system > 1)
+ argstr[x++] = 'x';
+ }
+ if (sparse_files)
+ argstr[x++] = 'S';
+ if (do_compression == 1)
+ argstr[x++] = 'z';
+
+ set_allow_inc_recurse();
+
+ /* We don't really know the actual protocol_version at this point,
+ * but checking the pre-negotiated value allows the user to use a
+ * --protocol=29 override to avoid the use of this -eFLAGS opt. */
+ if (protocol_version >= 30) {
+ /* We make use of the -e option to let the server know about
+ * any pre-release protocol version && some behavior flags. */
+ argstr[x++] = 'e';
+#if SUBPROTOCOL_VERSION != 0
+ if (protocol_version == PROTOCOL_VERSION) {
+ x += snprintf(argstr+x, sizeof argstr - x,
+ "%d.%d",
+ PROTOCOL_VERSION, SUBPROTOCOL_VERSION);
+ } else
+#endif
+ argstr[x++] = '.';
+ if (allow_inc_recurse)
+ argstr[x++] = 'i';
+#ifdef CAN_SET_SYMLINK_TIMES
+ argstr[x++] = 'L'; /* symlink time-setting support */
+#endif
+#ifdef ICONV_OPTION
+ argstr[x++] = 's'; /* symlink iconv translation support */
+#endif
+ argstr[x++] = 'f'; /* flist I/O-error safety support */
+ argstr[x++] = 'x'; /* xattr hardlink optimization not desired */
+ }
+
+ if (x >= (int)sizeof argstr) { /* Not possible... */
+ rprintf(FERROR, "argstr overflow in server_options().\n");
+ exit_cleanup(RERR_MALLOC);
+ }
+
+ argstr[x] = '\0';
+
+ if (x > 1)
+ args[ac++] = argstr;
+
+#ifdef ICONV_OPTION
+ if (iconv_opt) {
+ char *set = strchr(iconv_opt, ',');
+ if (set)
+ set++;
+ else
+ set = iconv_opt;
+ if (asprintf(&arg, "--iconv=%s", set) < 0)
+ goto oom;
+ args[ac++] = arg;
+ }
+#endif
+
+ if (protect_args && !local_server) /* unprotected args stop here */
+ args[ac++] = NULL;
+
+ if (list_only > 1)
+ args[ac++] = "--list-only";
+
+ /* This makes sure that the remote rsync can handle deleting with -d
+ * sans -r because the --no-r option was added at the same time. */
+ if (xfer_dirs && !recurse && delete_mode && am_sender)
+ args[ac++] = "--no-r";
+
+ if (do_compression && def_compress_level != Z_DEFAULT_COMPRESSION) {
+ if (asprintf(&arg, "--compress-level=%d", def_compress_level) < 0)
+ goto oom;
+ args[ac++] = arg;
+ }
+
+ if (preserve_devices) {
+ /* Note: sending "--devices" would not be backward-compatible. */
+ if (!preserve_specials)
+ args[ac++] = "--no-specials"; /* -D is already set. */
+ } else if (preserve_specials)
+ args[ac++] = "--specials";
+
+ /* The server side doesn't use our log-format, but in certain
+ * circumstances they need to know a little about the option. */
+ if (stdout_format && am_sender) {
+ /* Use --log-format, not --out-format, for compatibility. */
+ if (stdout_format_has_i > 1)
+ args[ac++] = "--log-format=%i%I";
+ else if (stdout_format_has_i)
+ args[ac++] = "--log-format=%i";
+ else if (stdout_format_has_o_or_i)
+ args[ac++] = "--log-format=%o";
+ else if (!verbose)
+ args[ac++] = "--log-format=X";
+ }
+
+ if (block_size) {
+ if (asprintf(&arg, "-B%lu", block_size) < 0)
+ goto oom;
+ args[ac++] = arg;
+ }
+
+ if (io_timeout) {
+ if (asprintf(&arg, "--timeout=%d", io_timeout) < 0)
+ goto oom;
+ args[ac++] = arg;
+ }
+
+ if (bwlimit) {
+ if (asprintf(&arg, "--bwlimit=%d", bwlimit) < 0)
+ goto oom;
+ args[ac++] = arg;
+ }
+
+ if (backup_dir) {
+ args[ac++] = "--backup-dir";
+ args[ac++] = backup_dir;
+ }
+
+ /* Only send --suffix if it specifies a non-default value. */
+ if (strcmp(backup_suffix, backup_dir ? "" : BACKUP_SUFFIX) != 0) {
+ /* We use the following syntax to avoid weirdness with '~'. */
+ if (asprintf(&arg, "--suffix=%s", backup_suffix) < 0)
+ goto oom;
+ args[ac++] = arg;
+ }
+
+ if (am_sender) {
+ if (max_delete > 0) {
+ if (asprintf(&arg, "--max-delete=%d", max_delete) < 0)
+ goto oom;
+ args[ac++] = arg;
+ } else if (max_delete == 0)
+ args[ac++] = "--max-delete=-1";
+ if (min_size >= 0) {
+ args[ac++] = "--min-size";
+ args[ac++] = min_size_arg;
+ }
+ if (max_size >= 0) {
+ args[ac++] = "--max-size";
+ args[ac++] = max_size_arg;
+ }
+ if (delete_before)
+ args[ac++] = "--delete-before";
+ else if (delete_during == 2)
+ args[ac++] = "--delete-delay";
+ else if (delete_during)
+ args[ac++] = "--delete-during";
+ else if (delete_after)
+ args[ac++] = "--delete-after";
+ else if (delete_mode && !delete_excluded)
+ args[ac++] = "--delete";
+ if (delete_excluded)
+ args[ac++] = "--delete-excluded";
+ if (force_delete)
+ args[ac++] = "--force";
+ if (write_batch < 0)
+ args[ac++] = "--only-write-batch=X";
+ if (am_root > 1)
+ args[ac++] = "--super";
+ if (size_only)
+ args[ac++] = "--size-only";
+ if (do_stats)
+ args[ac++] = "--stats";
+ } else {
+ if (skip_compress) {
+ if (asprintf(&arg, "--skip-compress=%s", skip_compress) < 0)
+ goto oom;
+ args[ac++] = arg;
+ }
+ }
+
+ /* --delete-missing-args needs the cooperation of both sides, but
+ * the sender can handle --ignore-missing-args by itself. */
+ if (missing_args == 2)
+ args[ac++] = "--delete-missing-args";
+ else if (missing_args == 1 && !am_sender)
+ args[ac++] = "--ignore-missing-args";
+
+ if (modify_window_set) {
+ if (asprintf(&arg, "--modify-window=%d", modify_window) < 0)
+ goto oom;
+ args[ac++] = arg;
+ }
+
+ if (checksum_seed) {
+ if (asprintf(&arg, "--checksum-seed=%d", checksum_seed) < 0)
+ goto oom;
+ args[ac++] = arg;
+ }
+
+ if (partial_dir && am_sender) {
+ if (partial_dir != tmp_partialdir) {
+ args[ac++] = "--partial-dir";
+ args[ac++] = partial_dir;
+ }
+ if (delay_updates)
+ args[ac++] = "--delay-updates";
+ } else if (keep_partial && am_sender)
+ args[ac++] = "--partial";
+
+ if (ignore_errors)
+ args[ac++] = "--ignore-errors";
+
+ if (copy_unsafe_links)
+ args[ac++] = "--copy-unsafe-links";
+
+ if (safe_symlinks)
+ args[ac++] = "--safe-links";
+
+ if (numeric_ids)
+ args[ac++] = "--numeric-ids";
+
+ if (use_qsort)
+ args[ac++] = "--use-qsort";
+
+ if (am_sender) {
+ if (usermap) {
+ if (asprintf(&arg, "--usermap=%s", usermap) < 0)
+ goto oom;
+ args[ac++] = arg;
+ }
+
+ if (groupmap) {
+ if (asprintf(&arg, "--groupmap=%s", groupmap) < 0)
+ goto oom;
+ args[ac++] = arg;
+ }
+
+ if (ignore_existing)
+ args[ac++] = "--ignore-existing";
+
+ /* Backward compatibility: send --existing, not --ignore-non-existing. */
+ if (ignore_non_existing)
+ args[ac++] = "--existing";
+
+ if (tmpdir) {
+ args[ac++] = "--temp-dir";
+ args[ac++] = tmpdir;
+ }
+
+ if (basis_dir[0]) {
+ /* the server only needs this option if it is not the sender,
+ * and it may be an older version that doesn't know this
+ * option, so don't send it if client is the sender.
+ */
+ for (i = 0; i < basis_dir_cnt; i++) {
+ args[ac++] = dest_option;
+ args[ac++] = basis_dir[i];
+ }
+ }
+ }
+
+ /* What flags do we need to send to the other side? */
+ where = (am_server ? W_CLI : W_SRV) | (am_sender ? W_REC : W_SND);
+ arg = make_output_option(info_words, info_levels, where);
+ if (arg)
+ args[ac++] = arg;
+
+ arg = make_output_option(debug_words, debug_levels, where);
+ if (arg)
+ args[ac++] = arg;
+
+ if (append_mode) {
+ if (append_mode > 1)
+ args[ac++] = "--append";
+ args[ac++] = "--append";
+ } else if (inplace)
+ args[ac++] = "--inplace";
+
+ if (files_from && (!am_sender || filesfrom_host)) {
+ if (filesfrom_host) {
+ args[ac++] = "--files-from";
+ args[ac++] = files_from;
+ if (eol_nulls)
+ args[ac++] = "--from0";
+ } else {
+ args[ac++] = "--files-from=-";
+ args[ac++] = "--from0";
+ }
+ if (!relative_paths)
+ args[ac++] = "--no-relative";
+ }
+ /* It's OK that this checks the upper-bound of the protocol_version. */
+ if (relative_paths && !implied_dirs && (!am_sender || protocol_version >= 30))
+ args[ac++] = "--no-implied-dirs";
+
+ if (remove_source_files == 1)
+ args[ac++] = "--remove-source-files";
+ else if (remove_source_files)
+ args[ac++] = "--remove-sent-files";
+
+ if (preallocate_files && am_sender)
+ args[ac++] = "--preallocate";
+
+ if (ac > MAX_SERVER_ARGS) { /* Not possible... */
+ rprintf(FERROR, "argc overflow in server_options().\n");
+ exit_cleanup(RERR_MALLOC);
+ }
+
+ if (do_compression > 1)
+ args[ac++] = "--new-compress";
+
+ if (remote_option_cnt) {
+ int j;
+ if (ac + remote_option_cnt > MAX_SERVER_ARGS) {
+ rprintf(FERROR, "too many remote options specified.\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ for (j = 1; j <= remote_option_cnt; j++)
+ args[ac++] = (char*)remote_options[j];
+ }
+
+ *argc_p = ac;
+ return;
+
+ oom:
+ out_of_memory("server_options");
+}
+
+/* If str points to a valid hostspec, return allocated memory containing the
+ * [USER@]HOST part of the string, and set the path_start_ptr to the part of
+ * the string after the host part. Otherwise, return NULL. If port_ptr is
+ * non-NULL, we must be parsing an rsync:// URL hostname, and we will set
+ * *port_ptr if a port number is found. Note that IPv6 IPs will have their
+ * (required for parsing) [ and ] chars elided from the returned string. */
+static char *parse_hostspec(char *str, char **path_start_ptr, int *port_ptr)
+{
+ char *s, *host_start = str;
+ int hostlen = 0, userlen = 0;
+ char *ret;
+
+ for (s = str; ; s++) {
+ if (!*s) {
+ /* It is only OK if we run out of string with rsync:// */
+ if (!port_ptr)
+ return NULL;
+ if (!hostlen)
+ hostlen = s - host_start;
+ break;
+ }
+ if (*s == ':' || *s == '/') {
+ if (!hostlen)
+ hostlen = s - host_start;
+ if (*s++ == '/') {
+ if (!port_ptr)
+ return NULL;
+ } else if (port_ptr) {
+ *port_ptr = atoi(s);
+ while (isDigit(s)) s++;
+ if (*s && *s++ != '/')
+ return NULL;
+ }
+ break;
+ }
+ if (*s == '@') {
+ userlen = s - str + 1;
+ host_start = s + 1;
+ } else if (*s == '[') {
+ if (s != host_start++)
+ return NULL;
+ while (*s && *s != ']' && *s != '/') s++; /*SHARED ITERATOR*/
+ hostlen = s - host_start;
+ if (*s != ']' || (s[1] && s[1] != '/' && s[1] != ':') || !hostlen)
+ return NULL;
+ }
+ }
+
+ *path_start_ptr = s;
+ ret = new_array(char, userlen + hostlen + 1);
+ if (userlen)
+ strlcpy(ret, str, userlen + 1);
+ strlcpy(ret + userlen, host_start, hostlen + 1);
+ return ret;
+}
+
+/* Look for a HOST specfication of the form "HOST:PATH", "HOST::PATH", or
+ * "rsync://HOST:PORT/PATH". If found, *host_ptr will be set to some allocated
+ * memory with the HOST. If a daemon-accessing spec was specified, the value
+ * of *port_ptr will contain a non-0 port number, otherwise it will be set to
+ * 0. The return value is a pointer to the PATH. Note that the HOST spec can
+ * be an IPv6 literal address enclosed in '[' and ']' (such as "[::1]" or
+ * "[::ffff:127.0.0.1]") which is returned without the '[' and ']'. */
+char *check_for_hostspec(char *s, char **host_ptr, int *port_ptr)
+{
+ char *path;
+
+ if (port_ptr && strncasecmp(URL_PREFIX, s, strlen(URL_PREFIX)) == 0) {
+ *host_ptr = parse_hostspec(s + strlen(URL_PREFIX), &path, port_ptr);
+ if (*host_ptr) {
+ if (!*port_ptr)
+ *port_ptr = RSYNC_PORT;
+ return path;
+ }
+ }
+
+ *host_ptr = parse_hostspec(s, &path, NULL);
+ if (!*host_ptr)
+ return NULL;
+
+ if (*path == ':') {
+ if (port_ptr && !*port_ptr)
+ *port_ptr = RSYNC_PORT;
+ return path + 1;
+ }
+ if (port_ptr)
+ *port_ptr = 0;
+
+ return path;
+}
diff --git a/rsync/params.c b/rsync/params.c
new file mode 100644
index 0000000..0fbd986
--- /dev/null
+++ b/rsync/params.c
@@ -0,0 +1,666 @@
+/* This modules is based on the params.c module from Samba, written by Karl Auer
+ and much modifed by Christopher Hertel. */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+/* -------------------------------------------------------------------------- **
+ *
+ * Module name: params
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * This module performs lexical analysis and initial parsing of a
+ * Windows-like parameter file. It recognizes and handles four token
+ * types: section-name, parameter-name, parameter-value, and
+ * end-of-file. Comments and line continuation are handled
+ * internally.
+ *
+ * The entry point to the module is function pm_process(). This
+ * function opens the source file, calls the Parse() function to parse
+ * the input, and then closes the file when either the EOF is reached
+ * or a fatal error is encountered.
+ *
+ * A sample parameter file might look like this:
+ *
+ * [section one]
+ * parameter one = value string
+ * parameter two = another value
+ * [section two]
+ * new parameter = some value or t'other
+ *
+ * The parameter file is divided into sections by section headers:
+ * section names enclosed in square brackets (eg. [section one]).
+ * Each section contains parameter lines, each of which consist of a
+ * parameter name and value delimited by an equal sign. Roughly, the
+ * syntax is:
+ *
+ * :== { } EOF
+ *
+ * :== { }
+ *
+ * :== '[' NAME ']'
+ *
+ * :== NAME '=' VALUE '\n'
+ *
+ * Blank lines and comment lines are ignored. Comment lines are lines
+ * beginning with either a semicolon (';') or a pound sign ('#').
+ *
+ * All whitespace in section names and parameter names is compressed
+ * to single spaces. Leading and trailing whitespace is stipped from
+ * both names and values.
+ *
+ * Only the first equals sign in a parameter line is significant.
+ * Parameter values may contain equals signs, square brackets and
+ * semicolons. Internal whitespace is retained in parameter values,
+ * with the exception of the '\r' character, which is stripped for
+ * historic reasons. Parameter names may not start with a left square
+ * bracket, an equal sign, a pound sign, or a semicolon, because these
+ * are used to identify other tokens.
+ *
+ * -------------------------------------------------------------------------- **
+ */
+
+#include "rsync.h"
+#include "ifuncs.h"
+#include "itypes.h"
+
+/* -------------------------------------------------------------------------- **
+ * Constants...
+ */
+
+#define BUFR_INC 1024
+
+
+/* -------------------------------------------------------------------------- **
+ * Variables...
+ *
+ * bufr - pointer to a global buffer. This is probably a kludge,
+ * but it was the nicest kludge I could think of (for now).
+ * bSize - The size of the global buffer .
+ */
+
+static char *bufr = NULL;
+static int bSize = 0;
+static BOOL (*the_sfunc)(char *);
+static BOOL (*the_pfunc)(char *, char *);
+
+/* -------------------------------------------------------------------------- **
+ * Functions...
+ */
+
+static int EatWhitespace( FILE *InFile )
+ /* ------------------------------------------------------------------------ **
+ * Scan past whitespace (see ctype(3C)) and return the first non-whitespace
+ * character, or newline, or EOF.
+ *
+ * Input: InFile - Input source.
+ *
+ * Output: The next non-whitespace character in the input stream.
+ *
+ * Notes: Because the config files use a line-oriented grammar, we
+ * explicitly exclude the newline character from the list of
+ * whitespace characters.
+ * - Note that both EOF (-1) and the nul character ('\0') are
+ * considered end-of-file markers.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int c;
+
+ for( c = getc( InFile ); isspace( c ) && ('\n' != c); c = getc( InFile ) )
+ ;
+ return( c );
+ } /* EatWhitespace */
+
+static int EatComment( FILE *InFile )
+ /* ------------------------------------------------------------------------ **
+ * Scan to the end of a comment.
+ *
+ * Input: InFile - Input source.
+ *
+ * Output: The character that marks the end of the comment. Normally,
+ * this will be a newline, but it *might* be an EOF.
+ *
+ * Notes: Because the config files use a line-oriented grammar, we
+ * explicitly exclude the newline character from the list of
+ * whitespace characters.
+ * - Note that both EOF (-1) and the nul character ('\0') are
+ * considered end-of-file markers.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int c;
+
+ for( c = getc( InFile ); ('\n'!=c) && (EOF!=c) && (c>0); c = getc( InFile ) )
+ ;
+ return( c );
+ } /* EatComment */
+
+static int Continuation( char *line, int pos )
+ /* ------------------------------------------------------------------------ **
+ * Scan backards within a string to discover if the last non-whitespace
+ * character is a line-continuation character ('\\').
+ *
+ * Input: line - A pointer to a buffer containing the string to be
+ * scanned.
+ * pos - This is taken to be the offset of the end of the
+ * string. This position is *not* scanned.
+ *
+ * Output: The offset of the '\\' character if it was found, or -1 to
+ * indicate that it was not.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ pos--;
+ while( pos >= 0 && isSpace(line + pos) )
+ pos--;
+
+ return( ((pos >= 0) && ('\\' == line[pos])) ? pos : -1 );
+ } /* Continuation */
+
+
+static BOOL Section( FILE *InFile, BOOL (*sfunc)(char *) )
+ /* ------------------------------------------------------------------------ **
+ * Scan a section name, and pass the name to function sfunc().
+ *
+ * Input: InFile - Input source.
+ * sfunc - Pointer to the function to be called if the section
+ * name is successfully read.
+ *
+ * Output: True if the section name was read and True was returned from
+ * . False if failed or if a lexical error was
+ * encountered.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int c;
+ int i;
+ int end;
+ char *func = "params.c:Section() -";
+
+ i = 0; /* is the offset of the next free byte in bufr[] and */
+ end = 0; /* is the current "end of string" offset. In most */
+ /* cases these will be the same, but if the last */
+ /* character written to bufr[] is a space, then */
+ /* will be one less than . */
+
+ c = EatWhitespace( InFile ); /* We've already got the '['. Scan */
+ /* past initial white space. */
+
+ while( (EOF != c) && (c > 0) )
+ {
+
+ /* Check that the buffer is big enough for the next character. */
+ if( i > (bSize - 2) )
+ {
+ bSize += BUFR_INC;
+ bufr = realloc_array( bufr, char, bSize );
+ if( NULL == bufr )
+ {
+ rprintf(FLOG, "%s Memory re-allocation failure.", func);
+ return( False );
+ }
+ }
+
+ /* Handle a single character. */
+ switch( c )
+ {
+ case ']': /* Found the closing bracket. */
+ bufr[end] = '\0';
+ if( 0 == end ) /* Don't allow an empty name. */
+ {
+ rprintf(FLOG, "%s Empty section name in config file.\n", func );
+ return( False );
+ }
+ if( !sfunc( bufr ) ) /* Got a valid name. Deal with it. */
+ return( False );
+ (void)EatComment( InFile ); /* Finish off the line. */
+ return( True );
+
+ case '\n': /* Got newline before closing ']'. */
+ i = Continuation( bufr, i ); /* Check for line continuation. */
+ if( i < 0 )
+ {
+ bufr[end] = '\0';
+ rprintf(FLOG, "%s Badly formed line in config file: %s\n",
+ func, bufr );
+ return( False );
+ }
+ end = ( (i > 0) && (' ' == bufr[i - 1]) ) ? (i - 1) : (i);
+ c = getc( InFile ); /* Continue with next line. */
+ break;
+
+ default: /* All else are a valid name chars. */
+ if( isspace( c ) ) /* One space per whitespace region. */
+ {
+ bufr[end] = ' ';
+ i = end + 1;
+ c = EatWhitespace( InFile );
+ }
+ else /* All others copy verbatim. */
+ {
+ bufr[i++] = c;
+ end = i;
+ c = getc( InFile );
+ }
+ }
+ }
+
+ /* We arrive here if we've met the EOF before the closing bracket. */
+ rprintf(FLOG, "%s Unexpected EOF in the config file: %s\n", func, bufr );
+ return( False );
+ } /* Section */
+
+static BOOL Parameter( FILE *InFile, BOOL (*pfunc)(char *, char *), int c )
+ /* ------------------------------------------------------------------------ **
+ * Scan a parameter name and value, and pass these two fields to pfunc().
+ *
+ * Input: InFile - The input source.
+ * pfunc - A pointer to the function that will be called to
+ * process the parameter, once it has been scanned.
+ * c - The first character of the parameter name, which
+ * would have been read by Parse(). Unlike a comment
+ * line or a section header, there is no lead-in
+ * character that can be discarded.
+ *
+ * Output: True if the parameter name and value were scanned and processed
+ * successfully, else False.
+ *
+ * Notes: This function is in two parts. The first loop scans the
+ * parameter name. Internal whitespace is compressed, and an
+ * equal sign (=) terminates the token. Leading and trailing
+ * whitespace is discarded. The second loop scans the parameter
+ * value. When both have been successfully identified, they are
+ * passed to pfunc() for processing.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int i = 0; /* Position within bufr. */
+ int end = 0; /* bufr[end] is current end-of-string. */
+ int vstart = 0; /* Starting position of the parameter value. */
+ char *func = "params.c:Parameter() -";
+
+ /* Read the parameter name. */
+ while( 0 == vstart ) /* Loop until we've found the start of the value. */
+ {
+
+ if( i > (bSize - 2) ) /* Ensure there's space for next char. */
+ {
+ bSize += BUFR_INC;
+ bufr = realloc_array( bufr, char, bSize );
+ if( NULL == bufr )
+ {
+ rprintf(FLOG, "%s Memory re-allocation failure.", func) ;
+ return( False );
+ }
+ }
+
+ switch( c )
+ {
+ case '=': /* Equal sign marks end of param name. */
+ if( 0 == end ) /* Don't allow an empty name. */
+ {
+ rprintf(FLOG, "%s Invalid parameter name in config file.\n", func );
+ return( False );
+ }
+ bufr[end++] = '\0'; /* Mark end of string & advance. */
+ i = vstart = end; /* New string starts here. */
+ c = EatWhitespace(InFile);
+ break;
+
+ case '\n': /* Find continuation char, else error. */
+ i = Continuation( bufr, i );
+ if( i < 0 )
+ {
+ bufr[end] = '\0';
+ rprintf(FLOG, "%s Ignoring badly formed line in config file: %s\n",
+ func, bufr );
+ return( True );
+ }
+ end = ( (i > 0) && (' ' == bufr[i - 1]) ) ? (i - 1) : (i);
+ c = getc( InFile ); /* Read past eoln. */
+ break;
+
+ case '\0': /* Shouldn't have EOF within param name. */
+ case EOF:
+ bufr[i] = '\0';
+ rprintf(FLOG, "%s Unexpected end-of-file at: %s\n", func, bufr );
+ return( True );
+
+ case ' ':
+ case '\t':
+ /* A directive divides at the first space or tab. */
+ if (*bufr == '&') {
+ bufr[end++] = '\0';
+ i = vstart = end;
+ c = EatWhitespace(InFile);
+ if (c == '=')
+ c = EatWhitespace(InFile);
+ break;
+ }
+ /* FALL THROUGH */
+
+ default:
+ if( isspace( c ) ) /* One ' ' per whitespace region. */
+ {
+ bufr[end] = ' ';
+ i = end + 1;
+ c = EatWhitespace( InFile );
+ }
+ else /* All others verbatim. */
+ {
+ bufr[i++] = c;
+ end = i;
+ c = getc( InFile );
+ }
+ }
+ }
+
+ /* Now parse the value. */
+ while( (EOF !=c) && (c > 0) )
+ {
+
+ if( i > (bSize - 2) ) /* Make sure there's enough room. */
+ {
+ bSize += BUFR_INC;
+ bufr = realloc_array( bufr, char, bSize );
+ if( NULL == bufr )
+ {
+ rprintf(FLOG, "%s Memory re-allocation failure.", func) ;
+ return( False );
+ }
+ }
+
+ switch( c )
+ {
+ case '\r': /* Explicitly remove '\r' because the older */
+ c = getc( InFile ); /* version called fgets_slash() which also */
+ break; /* removes them. */
+
+ case '\n': /* Marks end of value unless there's a '\'. */
+ i = Continuation( bufr, i );
+ if( i < 0 )
+ c = 0;
+ else
+ {
+ for( end = i; end >= 0 && isSpace(bufr + end); end-- )
+ ;
+ c = getc( InFile );
+ }
+ break;
+
+ default: /* All others verbatim. Note that spaces do */
+ bufr[i++] = c; /* not advance . This allows trimming */
+ if( !isspace( c ) ) /* of whitespace at the end of the line. */
+ end = i;
+ c = getc( InFile );
+ break;
+ }
+ }
+ bufr[end] = '\0'; /* End of value. */
+
+ return( pfunc( bufr, &bufr[vstart] ) ); /* Pass name & value to pfunc(). */
+ } /* Parameter */
+
+static int name_cmp(const void *n1, const void *n2)
+{
+ return strcmp(*(char * const *)n1, *(char * const *)n2);
+}
+
+static int include_config(char *include, int manage_globals)
+{
+ STRUCT_STAT sb;
+ char *match = manage_globals ? "*.conf" : "*.inc";
+ int ret;
+
+ if (do_stat(include, &sb) < 0) {
+ rsyserr(FLOG, errno, "unable to stat config file \"%s\"", include);
+ return 0;
+ }
+
+ if (S_ISREG(sb.st_mode)) {
+ if (manage_globals && the_sfunc)
+ the_sfunc("]push");
+ ret = pm_process(include, the_sfunc, the_pfunc);
+ if (manage_globals && the_sfunc)
+ the_sfunc("]pop");
+ } else if (S_ISDIR(sb.st_mode)) {
+ char buf[MAXPATHLEN], **bpp;
+ item_list conf_list;
+ struct dirent *di;
+ size_t j;
+ DIR *d;
+
+ if (!(d = opendir(include))) {
+ rsyserr(FLOG, errno, "unable to open config dir \"%s\"", include);
+ return 0;
+ }
+
+ memset(&conf_list, 0, sizeof conf_list);
+
+ while ((di = readdir(d)) != NULL) {
+ char *dname = d_name(di);
+ if (!wildmatch(match, dname))
+ continue;
+ bpp = EXPAND_ITEM_LIST(&conf_list, char *, 32);
+ pathjoin(buf, sizeof buf, include, dname);
+ *bpp = strdup(buf);
+ }
+ closedir(d);
+
+ if (!(bpp = conf_list.items))
+ return 1;
+
+ if (conf_list.count > 1)
+ qsort(bpp, conf_list.count, sizeof (char *), name_cmp);
+
+ for (j = 0, ret = 1; j < conf_list.count; j++) {
+ if (manage_globals && the_sfunc)
+ the_sfunc(j == 0 ? "]push" : "]reset");
+ if ((ret = pm_process(bpp[j], the_sfunc, the_pfunc)) != 1)
+ break;
+ }
+
+ if (manage_globals && the_sfunc)
+ the_sfunc("]pop");
+
+ for (j = 0; j < conf_list.count; j++)
+ free(bpp[j]);
+ free(bpp);
+ } else
+ ret = 0;
+
+ return ret;
+}
+
+static int parse_directives(char *name, char *val)
+{
+ if (strcasecmp(name, "&include") == 0)
+ return include_config(val, 1);
+ if (strcasecmp(name, "&merge") == 0)
+ return include_config(val, 0);
+ rprintf(FLOG, "Unknown directive: %s.\n", name);
+ return 0;
+}
+
+static int Parse( FILE *InFile,
+ BOOL (*sfunc)(char *),
+ BOOL (*pfunc)(char *, char *) )
+ /* ------------------------------------------------------------------------ **
+ * Scan & parse the input.
+ *
+ * Input: InFile - Input source.
+ * sfunc - Function to be called when a section name is scanned.
+ * See Section().
+ * pfunc - Function to be called when a parameter is scanned.
+ * See Parameter().
+ *
+ * Output: 1 if the file was successfully scanned, 2 if the file was
+ * scanned until a section header with no section function, else 0.
+ *
+ * Notes: The input can be viewed in terms of 'lines'. There are four
+ * types of lines:
+ * Blank - May contain whitespace, otherwise empty.
+ * Comment - First non-whitespace character is a ';' or '#'.
+ * The remainder of the line is ignored.
+ * Section - First non-whitespace character is a '['.
+ * Parameter - The default case.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int c;
+
+ c = EatWhitespace( InFile );
+ while( (EOF != c) && (c > 0) )
+ {
+ switch( c )
+ {
+ case '\n': /* Blank line. */
+ c = EatWhitespace( InFile );
+ break;
+
+ case ';': /* Comment line. */
+ case '#':
+ c = EatComment( InFile );
+ break;
+
+ case '[': /* Section Header. */
+ if (!sfunc)
+ return 2;
+ if( !Section( InFile, sfunc ) )
+ return 0;
+ c = EatWhitespace( InFile );
+ break;
+
+ case '\\': /* Bogus backslash. */
+ c = EatWhitespace( InFile );
+ break;
+
+ case '&': /* Handle directives */
+ the_sfunc = sfunc;
+ the_pfunc = pfunc;
+ c = Parameter( InFile, parse_directives, c );
+ if (c != 1)
+ return c;
+ c = EatWhitespace( InFile );
+ break;
+
+ default: /* Parameter line. */
+ if( !Parameter( InFile, pfunc, c ) )
+ return 0;
+ c = EatWhitespace( InFile );
+ break;
+ }
+ }
+ return 1;
+ } /* Parse */
+
+static FILE *OpenConfFile( char *FileName )
+ /* ------------------------------------------------------------------------ **
+ * Open a config file.
+ *
+ * Input: FileName - The pathname of the config file to be opened.
+ *
+ * Output: A pointer of type (FILE *) to the opened file, or NULL if the
+ * file could not be opened.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ FILE *OpenedFile;
+ char *func = "params.c:OpenConfFile() -";
+
+ if( NULL == FileName || 0 == *FileName )
+ {
+ rprintf(FLOG, "%s No config filename specified.\n", func);
+ return( NULL );
+ }
+
+ OpenedFile = fopen( FileName, "r" );
+ if( NULL == OpenedFile )
+ {
+ rsyserr(FLOG, errno, "unable to open config file \"%s\"",
+ FileName);
+ }
+
+ return( OpenedFile );
+ } /* OpenConfFile */
+
+int pm_process( char *FileName,
+ BOOL (*sfunc)(char *),
+ BOOL (*pfunc)(char *, char *) )
+ /* ------------------------------------------------------------------------ **
+ * Process the named parameter file.
+ *
+ * Input: FileName - The pathname of the parameter file to be opened.
+ * sfunc - A pointer to a function that will be called when
+ * a section name is discovered.
+ * pfunc - A pointer to a function that will be called when
+ * a parameter name and value are discovered.
+ *
+ * Output: 1 if the file was successfully parsed, 2 if parsing ended at a
+ * section header w/o a section function, else 0.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int result;
+ FILE *InFile;
+ char *func = "params.c:pm_process() -";
+
+ InFile = OpenConfFile( FileName ); /* Open the config file. */
+ if( NULL == InFile )
+ return( False );
+
+ if( NULL != bufr ) /* If we already have a buffer */
+ result = Parse( InFile, sfunc, pfunc ); /* (recursive call), then just */
+ /* use it. */
+
+ else /* If we don't have a buffer */
+ { /* allocate one, then parse, */
+ bSize = BUFR_INC; /* then free. */
+ bufr = new_array( char, bSize );
+ if( NULL == bufr )
+ {
+ rprintf(FLOG, "%s memory allocation failure.\n", func);
+ fclose(InFile);
+ return( False );
+ }
+ result = Parse( InFile, sfunc, pfunc );
+ free( bufr );
+ bufr = NULL;
+ bSize = 0;
+ }
+
+ fclose(InFile);
+
+ if( !result ) /* Generic failure. */
+ {
+ rprintf(FLOG, "%s Failed. Error returned from params.c:parse().\n", func);
+ return 0;
+ }
+
+ return result;
+ } /* pm_process */
+
+/* -------------------------------------------------------------------------- */
+
diff --git a/rsync/pipe.c b/rsync/pipe.c
new file mode 100644
index 0000000..4ac316a
--- /dev/null
+++ b/rsync/pipe.c
@@ -0,0 +1,180 @@
+/*
+ * Routines used to setup various kinds of inter-process pipes.
+ *
+ * Copyright (C) 1996-2000 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2001, 2002 Martin Pool
+ * Copyright (C) 2004-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+extern int am_sender;
+extern int am_server;
+extern int blocking_io;
+extern int filesfrom_fd;
+extern int munge_symlinks;
+extern char *logfile_name;
+extern int remote_option_cnt;
+extern const char **remote_options;
+extern struct chmod_mode_struct *chmod_modes;
+
+/**
+ * Create a child connected to us via its stdin/stdout.
+ *
+ * This is derived from CVS code
+ *
+ * Note that in the child STDIN is set to blocking and STDOUT
+ * is set to non-blocking. This is necessary as rsh relies on stdin being blocking
+ * and ssh relies on stdout being non-blocking
+ *
+ * If blocking_io is set then use blocking io on both fds. That can be
+ * used to cope with badly broken rsh implementations like the one on
+ * Solaris.
+ **/
+pid_t piped_child(char **command, int *f_in, int *f_out)
+{
+ pid_t pid;
+ int to_child_pipe[2];
+ int from_child_pipe[2];
+
+ if (DEBUG_GTE(CMD, 1))
+ print_child_argv("opening connection using:", command);
+
+ if (fd_pair(to_child_pipe) < 0 || fd_pair(from_child_pipe) < 0) {
+ rsyserr(FERROR, errno, "pipe");
+ exit_cleanup(RERR_IPC);
+ }
+
+ pid = do_fork();
+ if (pid == -1) {
+ rsyserr(FERROR, errno, "fork");
+ exit_cleanup(RERR_IPC);
+ }
+
+ if (pid == 0) {
+ if (dup2(to_child_pipe[0], STDIN_FILENO) < 0 ||
+ close(to_child_pipe[1]) < 0 ||
+ close(from_child_pipe[0]) < 0 ||
+ dup2(from_child_pipe[1], STDOUT_FILENO) < 0) {
+ rsyserr(FERROR, errno, "Failed to dup/close");
+ exit_cleanup(RERR_IPC);
+ }
+ if (to_child_pipe[0] != STDIN_FILENO)
+ close(to_child_pipe[0]);
+ if (from_child_pipe[1] != STDOUT_FILENO)
+ close(from_child_pipe[1]);
+ set_blocking(STDIN_FILENO);
+ if (blocking_io > 0)
+ set_blocking(STDOUT_FILENO);
+ execvp(command[0], command);
+ rsyserr(FERROR, errno, "Failed to exec %s", command[0]);
+ exit_cleanup(RERR_IPC);
+ }
+
+ if (close(from_child_pipe[1]) < 0 || close(to_child_pipe[0]) < 0) {
+ rsyserr(FERROR, errno, "Failed to close");
+ exit_cleanup(RERR_IPC);
+ }
+
+ *f_in = from_child_pipe[0];
+ *f_out = to_child_pipe[1];
+
+ return pid;
+}
+
+/* This function forks a child which calls child_main(). First,
+ * however, it has to establish communication paths to and from the
+ * newborn child. It creates two socket pairs -- one for writing to
+ * the child (from the parent) and one for reading from the child
+ * (writing to the parent). Since that's four socket ends, each
+ * process has to close the two ends it doesn't need. The remaining
+ * two socket ends are retained for reading and writing. In the
+ * child, the STDIN and STDOUT file descriptors refer to these
+ * sockets. In the parent, the function arguments f_in and f_out are
+ * set to refer to these sockets. */
+pid_t local_child(int argc, char **argv, int *f_in, int *f_out,
+ int (*child_main)(int, char*[]))
+{
+ pid_t pid;
+ int to_child_pipe[2];
+ int from_child_pipe[2];
+
+ /* The parent process is always the sender for a local rsync. */
+ assert(am_sender);
+
+ if (fd_pair(to_child_pipe) < 0 ||
+ fd_pair(from_child_pipe) < 0) {
+ rsyserr(FERROR, errno, "pipe");
+ exit_cleanup(RERR_IPC);
+ }
+
+ pid = do_fork();
+ if (pid == -1) {
+ rsyserr(FERROR, errno, "fork");
+ exit_cleanup(RERR_IPC);
+ }
+
+ if (pid == 0) {
+ am_sender = 0;
+ am_server = 1;
+ filesfrom_fd = -1;
+ munge_symlinks = 0; /* Each side needs its own option. */
+ chmod_modes = NULL; /* Let the sending side handle this. */
+
+ /* Let the client side handle this. */
+ if (logfile_name) {
+ logfile_name = NULL;
+ logfile_close();
+ }
+
+ if (remote_option_cnt) {
+ int rc = remote_option_cnt + 1;
+ const char **rv = remote_options;
+ if (!parse_arguments(&rc, &rv)) {
+ option_error();
+ exit_cleanup(RERR_SYNTAX);
+ }
+ }
+
+ if (dup2(to_child_pipe[0], STDIN_FILENO) < 0 ||
+ close(to_child_pipe[1]) < 0 ||
+ close(from_child_pipe[0]) < 0 ||
+ dup2(from_child_pipe[1], STDOUT_FILENO) < 0) {
+ rsyserr(FERROR, errno, "Failed to dup/close");
+ exit_cleanup(RERR_IPC);
+ }
+ if (to_child_pipe[0] != STDIN_FILENO)
+ close(to_child_pipe[0]);
+ if (from_child_pipe[1] != STDOUT_FILENO)
+ close(from_child_pipe[1]);
+#ifdef ICONV_CONST
+ setup_iconv();
+#endif
+ child_main(argc, argv);
+ }
+
+ if (close(from_child_pipe[1]) < 0 ||
+ close(to_child_pipe[0]) < 0) {
+ rsyserr(FERROR, errno, "Failed to close");
+ exit_cleanup(RERR_IPC);
+ }
+
+ *f_in = from_child_pipe[0];
+ *f_out = to_child_pipe[1];
+
+ return pid;
+}
diff --git a/rsync/popt/CHANGES b/rsync/popt/CHANGES
new file mode 100644
index 0000000..db16a5f
--- /dev/null
+++ b/rsync/popt/CHANGES
@@ -0,0 +1,46 @@
+1.5 -> 1.6
+ - add ability to perform callbacks for every, not just first, match.
+
+1.3 -> 1.5
+ - heavy dose of const's
+ - poptParseArgvString() now NULL terminates the list
+
+1.2.3 -> 1.3
+ - added support for single -
+ - misc bug fixes
+ - portability improvements
+
+1.2.2 -> 1.2.3
+ - fixed memset() in help message generation (Dale Hawkins)
+ - added extern "C" stuff to popt.h for C++ compilers (Dale Hawkins)
+ - const'ified poptParseArgvString (Jeff Garzik)
+
+1.2.1 -> 1.2.2
+ - fixed bug in chaind alias happens which seems to have only
+ affected --triggers in rpm
+ - added POPT_ARG_VAL
+ - popt.3 installed by default
+
+1.2 -> 1.2.1
+ - added POPT_ARG_INTL_DOMAIN (Elliot Lee)
+ - updated Makefile's to be more GNUish (Elliot Lee)
+
+1.1 -> 1.2
+ - added popt.3 man page (Robert Lynch)
+ - don't use mmap anymore (its lack of portability isn't worth the
+ trouble)
+ - added test script
+ - added support for exec
+ - removed support for *_POPT_ALIASES env variable -- it was a bad
+ idea
+ - reorganized into multiple source files
+ - added automatic help generation, POPT_AUTOHELP
+ - added table callbacks
+ - added table inclusion
+ - updated man page for new features
+ - added test scripts
+
+1.0 -> 1.1
+ - moved to autoconf (Fred Fish)
+ - added STRERROR replacement (Norbert Warmuth)
+ - added const keywords (Bruce Perens)
diff --git a/rsync/popt/COPYING b/rsync/popt/COPYING
new file mode 100644
index 0000000..b4c7ca8
--- /dev/null
+++ b/rsync/popt/COPYING
@@ -0,0 +1,22 @@
+Copyright (c) 1998 Red Hat Software
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
diff --git a/rsync/popt/README b/rsync/popt/README
new file mode 100644
index 0000000..0b5205b
--- /dev/null
+++ b/rsync/popt/README
@@ -0,0 +1,18 @@
+This is the popt command line option parsing library. While it is similiar
+to getopt(3), it contains a number of enhancements, including:
+
+ 1) popt is fully reentrant
+ 2) popt can parse arbitrary argv[] style arrays while
+ getopt(2) makes this quite difficult
+ 3) popt allows users to alias command line arguments
+ 4) popt provides convience functions for parsing strings
+ into argv[] style arrays
+
+popt is used by rpm, the Red Hat install program, and many other Red Hat
+utilities, all of which provide excellent examples of how to use popt.
+Complete documentation on popt is available in popt.ps (included in this
+tarball), which is excerpted with permission from the book "Linux
+Application Development" by Michael K. Johnson and Erik Troan (availble
+from Addison Wesley in May, 1998).
+
+Comments on popt should be addressed to ewt@redhat.com.
diff --git a/rsync/popt/README.rsync b/rsync/popt/README.rsync
new file mode 100644
index 0000000..1cf54d5
--- /dev/null
+++ b/rsync/popt/README.rsync
@@ -0,0 +1,4 @@
+This is a perfectly ordinary copy of libpopt. It is only used on platforms
+that do not have a sufficiently up-to-date copy of their own. If you build
+rsync on a platform which has popt, this directory should not be used. (You
+can control that using the --with-included-popt configure flag.)
diff --git a/rsync/popt/dummy.in b/rsync/popt/dummy.in
new file mode 100644
index 0000000..e69de29
diff --git a/rsync/popt/findme.c b/rsync/popt/findme.c
new file mode 100644
index 0000000..ac4cbae
--- /dev/null
+++ b/rsync/popt/findme.c
@@ -0,0 +1,55 @@
+/** \ingroup popt
+ * \file popt/findme.c
+ */
+
+/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.rpm.org/pub/rpm/dist. */
+
+#include "system.h"
+#include "findme.h"
+
+const char * findProgramPath(const char * argv0)
+{
+ char * path = getenv("PATH");
+ char * pathbuf;
+ char * start, * chptr;
+ char * buf;
+ size_t bufsize;
+
+ if (argv0 == NULL) return NULL; /* XXX can't happen */
+ /* If there is a / in the argv[0], it has to be an absolute path */
+ if (strchr(argv0, '/'))
+ return xstrdup(argv0);
+
+ if (path == NULL) return NULL;
+
+ bufsize = strlen(path) + 1;
+ start = pathbuf = alloca(bufsize);
+ if (pathbuf == NULL) return NULL; /* XXX can't happen */
+ strlcpy(pathbuf, path, bufsize);
+ bufsize += sizeof "/" - 1 + strlen(argv0);
+ buf = malloc(bufsize);
+ if (buf == NULL) return NULL; /* XXX can't happen */
+
+ chptr = NULL;
+ /*@-branchstate@*/
+ do {
+ if ((chptr = strchr(start, ':')))
+ *chptr = '\0';
+ snprintf(buf, bufsize, "%s/%s", start, argv0);
+
+ if (!access(buf, X_OK))
+ return buf;
+
+ if (chptr)
+ start = chptr + 1;
+ else
+ start = NULL;
+ } while (start && *start);
+ /*@=branchstate@*/
+
+ free(buf);
+
+ return NULL;
+}
diff --git a/rsync/popt/findme.h b/rsync/popt/findme.h
new file mode 100644
index 0000000..a016b86
--- /dev/null
+++ b/rsync/popt/findme.h
@@ -0,0 +1,20 @@
+/** \ingroup popt
+ * \file popt/findme.h
+ */
+
+/* (C) 1998-2000 Red Hat, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.rpm.org/pub/rpm/dist. */
+
+#ifndef H_FINDME
+#define H_FINDME
+
+/**
+ * Return absolute path to executable by searching PATH.
+ * @param argv0 name of executable
+ * @return (malloc'd) absolute path to executable (or NULL)
+ */
+/*@null@*/ const char * findProgramPath(/*@null@*/ const char * argv0)
+ /*@*/;
+
+#endif
diff --git a/rsync/popt/popt.c b/rsync/popt/popt.c
new file mode 100644
index 0000000..ec6f3bd
--- /dev/null
+++ b/rsync/popt/popt.c
@@ -0,0 +1,1283 @@
+/** \ingroup popt
+ * \file popt/popt.c
+ */
+
+/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.rpm.org/pub/rpm/dist */
+
+#undef MYDEBUG
+
+#include "system.h"
+
+#if HAVE_FLOAT_H
+#include
+#endif
+#include
+
+#include "findme.h"
+#include "poptint.h"
+
+#ifndef DBL_EPSILON
+#define DBL_EPSILON 2.2204460492503131e-16
+#endif
+
+#ifdef MYDEBUG
+/*@unchecked@*/
+int _popt_debug = 0;
+#endif
+
+#if !defined(HAVE_STRERROR) && !defined(__LCLINT__)
+static char * strerror(int errno)
+{
+ extern int sys_nerr;
+ extern char * sys_errlist[];
+
+ if ((0 <= errno) && (errno < sys_nerr))
+ return sys_errlist[errno];
+ else
+ return POPT_("unknown errno");
+}
+#endif
+
+#ifdef MYDEBUG
+/*@unused@*/
+static void prtcon(const char *msg, poptContext con)
+{
+ if (msg) fprintf(stderr, "%s", msg);
+ fprintf(stderr, "\tcon %p os %p nextCharArg \"%s\" nextArg \"%s\" argv[%d] \"%s\"\n",
+ con, con->os,
+ (con->os->nextCharArg ? con->os->nextCharArg : ""),
+ (con->os->nextArg ? con->os->nextArg : ""),
+ con->os->next,
+ (con->os->argv && con->os->argv[con->os->next]
+ ? con->os->argv[con->os->next] : ""));
+}
+#endif
+
+void poptSetExecPath(poptContext con, const char * path, int allowAbsolute)
+{
+ con->execPath = _free(con->execPath);
+ con->execPath = xstrdup(path);
+ con->execAbsolute = allowAbsolute;
+ /*@-nullstate@*/ /* LCL: con->execPath not NULL */
+ return;
+ /*@=nullstate@*/
+}
+
+static void invokeCallbacksPRE(poptContext con, const struct poptOption * opt)
+ /*@globals internalState@*/
+ /*@modifies internalState@*/
+{
+ if (opt != NULL)
+ for (; opt->longName || opt->shortName || opt->arg; opt++) {
+ if (opt->arg == NULL) continue; /* XXX program error. */
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+ void * arg = opt->arg;
+/*@-branchstate@*/
+ /* XXX sick hack to preserve pretense of ABI. */
+ if (arg == poptHelpOptions) arg = poptHelpOptionsI18N;
+/*@=branchstate@*/
+ /* Recurse on included sub-tables. */
+ invokeCallbacksPRE(con, arg);
+ } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK &&
+ (opt->argInfo & POPT_CBFLAG_PRE))
+ { /*@-castfcnptr@*/
+ poptCallbackType cb = (poptCallbackType)opt->arg;
+ /*@=castfcnptr@*/
+ /* Perform callback. */
+ /*@-noeffectuncon @*/
+ cb(con, POPT_CALLBACK_REASON_PRE, NULL, NULL, opt->descrip);
+ /*@=noeffectuncon @*/
+ }
+ }
+}
+
+static void invokeCallbacksPOST(poptContext con, const struct poptOption * opt)
+ /*@globals internalState@*/
+ /*@modifies internalState@*/
+{
+ if (opt != NULL)
+ for (; opt->longName || opt->shortName || opt->arg; opt++) {
+ if (opt->arg == NULL) continue; /* XXX program error. */
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+ void * arg = opt->arg;
+/*@-branchstate@*/
+ /* XXX sick hack to preserve pretense of ABI. */
+ if (arg == poptHelpOptions) arg = poptHelpOptionsI18N;
+/*@=branchstate@*/
+ /* Recurse on included sub-tables. */
+ invokeCallbacksPOST(con, arg);
+ } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK &&
+ (opt->argInfo & POPT_CBFLAG_POST))
+ { /*@-castfcnptr@*/
+ poptCallbackType cb = (poptCallbackType)opt->arg;
+ /*@=castfcnptr@*/
+ /* Perform callback. */
+ /*@-noeffectuncon @*/
+ cb(con, POPT_CALLBACK_REASON_POST, NULL, NULL, opt->descrip);
+ /*@=noeffectuncon @*/
+ }
+ }
+}
+
+static void invokeCallbacksOPTION(poptContext con,
+ const struct poptOption * opt,
+ const struct poptOption * myOpt,
+ /*@null@*/ const void * myData, int shorty)
+ /*@globals internalState@*/
+ /*@modifies internalState@*/
+{
+ const struct poptOption * cbopt = NULL;
+
+ if (opt != NULL)
+ for (; opt->longName || opt->shortName || opt->arg; opt++) {
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+ void * arg = opt->arg;
+/*@-branchstate@*/
+ /* XXX sick hack to preserve pretense of ABI. */
+ if (arg == poptHelpOptions) arg = poptHelpOptionsI18N;
+/*@=branchstate@*/
+ /* Recurse on included sub-tables. */
+ if (opt->arg != NULL) /* XXX program error */
+ invokeCallbacksOPTION(con, opt->arg, myOpt, myData, shorty);
+ } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK &&
+ !(opt->argInfo & POPT_CBFLAG_SKIPOPTION)) {
+ /* Save callback info. */
+ cbopt = opt;
+ } else if (cbopt != NULL &&
+ ((myOpt->shortName && opt->shortName && shorty &&
+ myOpt->shortName == opt->shortName) ||
+ (myOpt->longName && opt->longName &&
+ /*@-nullpass@*/ /* LCL: opt->longName != NULL */
+ !strcmp(myOpt->longName, opt->longName)))
+ /*@=nullpass@*/
+ )
+ { /*@-castfcnptr@*/
+ poptCallbackType cb = (poptCallbackType)cbopt->arg;
+ /*@=castfcnptr@*/
+ const void * cbData = (cbopt->descrip ? cbopt->descrip : myData);
+ /* Perform callback. */
+ if (cb != NULL) { /* XXX program error */
+ /*@-noeffectuncon @*/
+ cb(con, POPT_CALLBACK_REASON_OPTION, myOpt,
+ con->os->nextArg, cbData);
+ /*@=noeffectuncon @*/
+ }
+ /* Terminate (unless explcitly continuing). */
+ if (!(cbopt->argInfo & POPT_CBFLAG_CONTINUE))
+ return;
+ }
+ }
+}
+
+poptContext poptGetContext(const char * name, int argc, const char ** argv,
+ const struct poptOption * options, int flags)
+{
+ poptContext con = malloc(sizeof(*con));
+
+ if (con == NULL) return NULL; /* XXX can't happen */
+ memset(con, 0, sizeof(*con));
+
+ con->os = con->optionStack;
+ con->os->argc = argc;
+ /*@-dependenttrans -assignexpose@*/ /* FIX: W2DO? */
+ con->os->argv = argv;
+ /*@=dependenttrans =assignexpose@*/
+ con->os->argb = NULL;
+
+ if (!(flags & POPT_CONTEXT_KEEP_FIRST))
+ con->os->next = 1; /* skip argv[0] */
+
+ con->leftovers = calloc( (argc + 1), sizeof(*con->leftovers) );
+ /*@-dependenttrans -assignexpose@*/ /* FIX: W2DO? */
+ con->options = options;
+ /*@=dependenttrans =assignexpose@*/
+ con->aliases = NULL;
+ con->numAliases = 0;
+ con->flags = flags;
+ con->execs = NULL;
+ con->numExecs = 0;
+ con->finalArgvAlloced = argc * 2;
+ con->finalArgv = calloc( con->finalArgvAlloced, sizeof(*con->finalArgv) );
+ con->execAbsolute = 1;
+ con->arg_strip = NULL;
+
+ if (getenv("POSIXLY_CORRECT") || getenv("POSIX_ME_HARDER"))
+ con->flags |= POPT_CONTEXT_POSIXMEHARDER;
+
+ if (name) {
+ size_t bufsize = strlen(name) + 1;
+ char * t = malloc(bufsize);
+ if (t) {
+ strlcpy(t, name, bufsize);
+ con->appName = t;
+ }
+ }
+
+ /*@-internalglobs@*/
+ invokeCallbacksPRE(con, con->options);
+ /*@=internalglobs@*/
+
+ return con;
+}
+
+static void cleanOSE(/*@special@*/ struct optionStackEntry *os)
+ /*@uses os @*/
+ /*@releases os->nextArg, os->argv, os->argb @*/
+ /*@modifies os @*/
+{
+ os->nextArg = _free(os->nextArg);
+ os->argv = _free(os->argv);
+ os->argb = PBM_FREE(os->argb);
+}
+
+/*@-boundswrite@*/
+void poptResetContext(poptContext con)
+{
+ int i;
+
+ if (con == NULL) return;
+ while (con->os > con->optionStack) {
+ cleanOSE(con->os--);
+ }
+ con->os->argb = PBM_FREE(con->os->argb);
+ con->os->currAlias = NULL;
+ con->os->nextCharArg = NULL;
+ con->os->nextArg = NULL;
+ con->os->next = 1; /* skip argv[0] */
+
+ con->numLeftovers = 0;
+ con->nextLeftover = 0;
+ con->restLeftover = 0;
+ con->doExec = NULL;
+
+ if (con->finalArgv != NULL)
+ for (i = 0; i < con->finalArgvCount; i++) {
+ /*@-unqualifiedtrans@*/ /* FIX: typedef double indirection. */
+ con->finalArgv[i] = _free(con->finalArgv[i]);
+ /*@=unqualifiedtrans@*/
+ }
+
+ con->finalArgvCount = 0;
+ con->arg_strip = PBM_FREE(con->arg_strip);
+ /*@-nullstate@*/ /* FIX: con->finalArgv != NULL */
+ return;
+ /*@=nullstate@*/
+}
+/*@=boundswrite@*/
+
+/* Only one of longName, shortName should be set, not both. */
+/*@-boundswrite@*/
+static int handleExec(/*@special@*/ poptContext con,
+ /*@null@*/ const char * longName, char shortName)
+ /*@uses con->execs, con->numExecs, con->flags, con->doExec,
+ con->finalArgv, con->finalArgvAlloced, con->finalArgvCount @*/
+ /*@modifies con @*/
+{
+ poptItem item;
+ int i;
+
+ if (con->execs == NULL || con->numExecs <= 0) /* XXX can't happen */
+ return 0;
+
+ for (i = con->numExecs - 1; i >= 0; i--) {
+ item = con->execs + i;
+ if (longName && !(item->option.longName &&
+ !strcmp(longName, item->option.longName)))
+ continue;
+ else if (shortName != item->option.shortName)
+ continue;
+ break;
+ }
+ if (i < 0) return 0;
+
+
+ if (con->flags & POPT_CONTEXT_NO_EXEC)
+ return 1;
+
+ if (con->doExec == NULL) {
+ con->doExec = con->execs + i;
+ return 1;
+ }
+
+ /* We already have an exec to do; remember this option for next
+ time 'round */
+ if ((con->finalArgvCount + 1) >= (con->finalArgvAlloced)) {
+ con->finalArgvAlloced += 10;
+ con->finalArgv = realloc(con->finalArgv,
+ sizeof(*con->finalArgv) * con->finalArgvAlloced);
+ }
+
+ i = con->finalArgvCount++;
+ if (con->finalArgv != NULL) /* XXX can't happen */
+ { size_t bufsize = (longName ? strlen(longName) : 0) + 3;
+ char *s = malloc(bufsize);
+ if (s != NULL) { /* XXX can't happen */
+ if (longName)
+ snprintf(s, bufsize, "--%s", longName);
+ else
+ snprintf(s, bufsize, "-%c", shortName);
+ con->finalArgv[i] = s;
+ } else
+ con->finalArgv[i] = NULL;
+ }
+
+ /*@-nullstate@*/ /* FIX: con->finalArgv[] == NULL */
+ return 1;
+ /*@=nullstate@*/
+}
+/*@=boundswrite@*/
+
+/* Only one of longName, shortName may be set at a time */
+static int handleAlias(/*@special@*/ poptContext con,
+ /*@null@*/ const char * longName, char shortName,
+ /*@exposed@*/ /*@null@*/ const char * nextCharArg)
+ /*@uses con->aliases, con->numAliases, con->optionStack, con->os,
+ con->os->currAlias, con->os->currAlias->option.longName @*/
+ /*@modifies con @*/
+{
+ poptItem item = con->os->currAlias;
+ int rc;
+ int i;
+
+ if (item) {
+ if (longName && (item->option.longName &&
+ !strcmp(longName, item->option.longName)))
+ return 0;
+ if (shortName && shortName == item->option.shortName)
+ return 0;
+ }
+
+ if (con->aliases == NULL || con->numAliases <= 0) /* XXX can't happen */
+ return 0;
+
+ for (i = con->numAliases - 1; i >= 0; i--) {
+ item = con->aliases + i;
+ if (longName && !(item->option.longName &&
+ !strcmp(longName, item->option.longName)))
+ continue;
+ else if (shortName != item->option.shortName)
+ continue;
+ break;
+ }
+ if (i < 0) return 0;
+
+ if ((con->os - con->optionStack + 1) == POPT_OPTION_DEPTH)
+ return POPT_ERROR_OPTSTOODEEP;
+
+/*@-boundsread@*/
+ if (nextCharArg && *nextCharArg)
+ con->os->nextCharArg = nextCharArg;
+/*@=boundsread@*/
+
+ con->os++;
+ con->os->next = 0;
+ con->os->stuffed = 0;
+ con->os->nextArg = NULL;
+ con->os->nextCharArg = NULL;
+ con->os->currAlias = con->aliases + i;
+ rc = poptDupArgv(con->os->currAlias->argc, con->os->currAlias->argv,
+ &con->os->argc, &con->os->argv);
+ con->os->argb = NULL;
+
+ return (rc ? rc : 1);
+}
+
+/*@-bounds -boundswrite @*/
+static int execCommand(poptContext con)
+ /*@globals internalState @*/
+ /*@modifies internalState @*/
+{
+ poptItem item = con->doExec;
+ const char ** argv;
+ int argc = 0;
+
+ if (item == NULL) /*XXX can't happen*/
+ return POPT_ERROR_NOARG;
+
+ if (item->argv == NULL || item->argc < 1 ||
+ (!con->execAbsolute && strchr(item->argv[0], '/')))
+ return POPT_ERROR_NOARG;
+
+ argv = malloc(sizeof(*argv) *
+ (6 + item->argc + con->numLeftovers + con->finalArgvCount));
+ if (argv == NULL) return POPT_ERROR_MALLOC;
+
+ if (!strchr(item->argv[0], '/') && con->execPath != NULL) {
+ size_t bufsize = strlen(con->execPath) + strlen(item->argv[0]) + sizeof "/";
+ char *s = alloca(bufsize);
+ snprintf(s, bufsize, "%s/%s", con->execPath, item->argv[0]);
+ argv[argc] = s;
+ } else
+ argv[argc] = findProgramPath(item->argv[0]);
+ if (argv[argc++] == NULL) return POPT_ERROR_NOARG;
+
+ if (item->argc > 1) {
+ memcpy(argv + argc, item->argv + 1, sizeof(*argv) * (item->argc - 1));
+ argc += (item->argc - 1);
+ }
+
+ if (con->finalArgv != NULL && con->finalArgvCount > 0) {
+ memcpy(argv + argc, con->finalArgv,
+ sizeof(*argv) * con->finalArgvCount);
+ argc += con->finalArgvCount;
+ }
+
+ if (con->leftovers != NULL && con->numLeftovers > 0) {
+ memcpy(argv + argc, con->leftovers, sizeof(*argv) * con->numLeftovers);
+ argc += con->numLeftovers;
+ }
+
+ argv[argc] = NULL;
+
+ {
+#ifdef __hpux
+ int rc = setresgid(getgid(), getgid(),-1);
+ if (rc) return POPT_ERROR_ERRNO;
+ rc = setresuid(getuid(), getuid(),-1);
+ if (rc) return POPT_ERROR_ERRNO;
+#else
+/*
+ * XXX " ... on BSD systems setuid() should be preferred over setreuid()"
+ * XXX sez' Timur Bakeyev
+ * XXX from Norbert Warmuth
+ */
+#if defined(HAVE_SETUID)
+ int rc = setgid(getgid());
+ if (rc) return POPT_ERROR_ERRNO;
+ rc = setuid(getuid());
+ if (rc) return POPT_ERROR_ERRNO;
+#elif defined (HAVE_SETREUID)
+ int rc = setregid(getgid(), getgid());
+ if (rc) return POPT_ERROR_ERRNO;
+ rc = setreuid(getuid(), getuid());
+ if (rc) return POPT_ERROR_ERRNO;
+#else
+ ; /* Can't drop privileges */
+#endif
+#endif
+ }
+
+ if (argv[0] == NULL)
+ return POPT_ERROR_NOARG;
+
+#ifdef MYDEBUG
+if (_popt_debug)
+ { const char ** avp;
+ fprintf(stderr, "==> execvp(%s) argv[%d]:", argv[0], argc);
+ for (avp = argv; *avp; avp++)
+ fprintf(stderr, " '%s'", *avp);
+ fprintf(stderr, "\n");
+ }
+#endif
+
+ execvp(argv[0], (char *const *)argv);
+
+ return POPT_ERROR_ERRNO;
+}
+/*@=bounds =boundswrite @*/
+
+/*@-boundswrite@*/
+/*@observer@*/ /*@null@*/ static const struct poptOption *
+findOption(const struct poptOption * opt, /*@null@*/ const char * longName,
+ char shortName,
+ /*@null@*/ /*@out@*/ poptCallbackType * callback,
+ /*@null@*/ /*@out@*/ const void ** callbackData,
+ int singleDash)
+ /*@modifies *callback, *callbackData */
+{
+ const struct poptOption * cb = NULL;
+
+ /* This happens when a single - is given */
+ if (singleDash && !shortName && (longName && *longName == '\0'))
+ shortName = '-';
+
+ for (; opt->longName || opt->shortName || opt->arg; opt++) {
+
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+ const struct poptOption * opt2;
+ void * arg = opt->arg;
+
+/*@-branchstate@*/
+ /* XXX sick hack to preserve pretense of ABI. */
+ if (arg == poptHelpOptions) arg = poptHelpOptionsI18N;
+/*@=branchstate@*/
+ /* Recurse on included sub-tables. */
+ if (arg == NULL) continue; /* XXX program error */
+ opt2 = findOption(arg, longName, shortName, callback,
+ callbackData, singleDash);
+ if (opt2 == NULL) continue;
+ /* Sub-table data will be inheirited if no data yet. */
+ if (!(callback && *callback)) return opt2;
+ if (!(callbackData && *callbackData == NULL)) return opt2;
+ /*@-observertrans -dependenttrans @*/
+ *callbackData = opt->descrip;
+ /*@=observertrans =dependenttrans @*/
+ return opt2;
+ } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) {
+ cb = opt;
+ } else if (longName && opt->longName &&
+ (!singleDash || (opt->argInfo & POPT_ARGFLAG_ONEDASH)) &&
+ /*@-nullpass@*/ /* LCL: opt->longName != NULL */
+ !strcmp(longName, opt->longName))
+ /*@=nullpass@*/
+ {
+ break;
+ } else if (shortName && shortName == opt->shortName) {
+ break;
+ }
+ }
+
+ if (!opt->longName && !opt->shortName)
+ return NULL;
+ /*@-modobserver -mods @*/
+ if (callback) *callback = NULL;
+ if (callbackData) *callbackData = NULL;
+ if (cb) {
+ if (callback)
+ /*@-castfcnptr@*/
+ *callback = (poptCallbackType)cb->arg;
+ /*@=castfcnptr@*/
+ if (!(cb->argInfo & POPT_CBFLAG_INC_DATA)) {
+ if (callbackData)
+ /*@-observertrans@*/ /* FIX: typedef double indirection. */
+ *callbackData = cb->descrip;
+ /*@=observertrans@*/
+ }
+ }
+ /*@=modobserver =mods @*/
+
+ return opt;
+}
+/*@=boundswrite@*/
+
+static const char * findNextArg(/*@special@*/ poptContext con,
+ unsigned argx, int delete_arg)
+ /*@uses con->optionStack, con->os,
+ con->os->next, con->os->argb, con->os->argc, con->os->argv @*/
+ /*@modifies con @*/
+{
+ struct optionStackEntry * os = con->os;
+ const char * arg;
+
+ do {
+ int i;
+ arg = NULL;
+ while (os->next == os->argc && os > con->optionStack) os--;
+ if (os->next == os->argc && os == con->optionStack) break;
+ if (os->argv != NULL)
+ for (i = os->next; i < os->argc; i++) {
+ /*@-sizeoftype@*/
+ if (os->argb && PBM_ISSET(i, os->argb))
+ /*@innercontinue@*/ continue;
+ if (*os->argv[i] == '-')
+ /*@innercontinue@*/ continue;
+ if (--argx > 0)
+ /*@innercontinue@*/ continue;
+ arg = os->argv[i];
+ if (delete_arg) {
+ if (os->argb == NULL) os->argb = PBM_ALLOC(os->argc);
+ if (os->argb != NULL) /* XXX can't happen */
+ PBM_SET(i, os->argb);
+ }
+ /*@innerbreak@*/ break;
+ /*@=sizeoftype@*/
+ }
+ if (os > con->optionStack) os--;
+ } while (arg == NULL);
+ return arg;
+}
+
+/*@-boundswrite@*/
+static /*@only@*/ /*@null@*/ const char *
+expandNextArg(/*@special@*/ poptContext con, const char * s)
+ /*@uses con->optionStack, con->os,
+ con->os->next, con->os->argb, con->os->argc, con->os->argv @*/
+ /*@modifies con @*/
+{
+ const char * a = NULL;
+ size_t alen, pos;
+ char *t, *te;
+ size_t tn = strlen(s) + 1;
+ char c;
+
+ te = t = malloc(tn);;
+ if (t == NULL) return NULL; /* XXX can't happen */
+ while ((c = *s++) != '\0') {
+ switch (c) {
+#if 0 /* XXX can't do this */
+ case '\\': /* escape */
+ c = *s++;
+ /*@switchbreak@*/ break;
+#endif
+ case '!':
+ if (!(s[0] == '#' && s[1] == ':' && s[2] == '+'))
+ /*@switchbreak@*/ break;
+ /* XXX Make sure that findNextArg deletes only next arg. */
+ if (a == NULL) {
+ if ((a = findNextArg(con, 1, 1)) == NULL)
+ /*@switchbreak@*/ break;
+ }
+ s += 3;
+
+ alen = strlen(a);
+ tn += alen;
+ pos = te - t;
+ t = realloc(t, tn);
+ te = t + pos;
+ strncpy(te, a, alen); te += alen;
+ continue;
+ /*@notreached@*/ /*@switchbreak@*/ break;
+ default:
+ /*@switchbreak@*/ break;
+ }
+ *te++ = c;
+ }
+ *te = '\0';
+ t = realloc(t, strlen(t) + 1); /* XXX memory leak, hard to plug */
+ return t;
+}
+/*@=boundswrite@*/
+
+static void poptStripArg(/*@special@*/ poptContext con, int which)
+ /*@uses con->arg_strip, con->optionStack @*/
+ /*@defines con->arg_strip @*/
+ /*@modifies con @*/
+{
+ /*@-sizeoftype@*/
+ if (con->arg_strip == NULL)
+ con->arg_strip = PBM_ALLOC(con->optionStack[0].argc);
+ if (con->arg_strip != NULL) /* XXX can't happen */
+ PBM_SET(which, con->arg_strip);
+ /*@=sizeoftype@*/
+ /*@-compdef@*/ /* LCL: con->arg_strip udefined? */
+ return;
+ /*@=compdef@*/
+}
+
+int poptSaveLong(long * arg, int argInfo, long aLong)
+{
+ /* XXX Check alignment, may fail on funky platforms. */
+ if (arg == NULL || (((unsigned long)arg) & (sizeof(*arg)-1)))
+ return POPT_ERROR_NULLARG;
+
+ if (argInfo & POPT_ARGFLAG_NOT)
+ aLong = ~aLong;
+ switch (argInfo & POPT_ARGFLAG_LOGICALOPS) {
+ case 0:
+ *arg = aLong;
+ break;
+ case POPT_ARGFLAG_OR:
+ *arg |= aLong;
+ break;
+ case POPT_ARGFLAG_AND:
+ *arg &= aLong;
+ break;
+ case POPT_ARGFLAG_XOR:
+ *arg ^= aLong;
+ break;
+ default:
+ return POPT_ERROR_BADOPERATION;
+ /*@notreached@*/ break;
+ }
+ return 0;
+}
+
+int poptSaveInt(/*@null@*/ int * arg, int argInfo, long aLong)
+{
+ /* XXX Check alignment, may fail on funky platforms. */
+ if (arg == NULL || (((unsigned long)arg) & (sizeof(*arg)-1)))
+ return POPT_ERROR_NULLARG;
+
+ if (argInfo & POPT_ARGFLAG_NOT)
+ aLong = ~aLong;
+ switch (argInfo & POPT_ARGFLAG_LOGICALOPS) {
+ case 0:
+ *arg = aLong;
+ break;
+ case POPT_ARGFLAG_OR:
+ *arg |= aLong;
+ break;
+ case POPT_ARGFLAG_AND:
+ *arg &= aLong;
+ break;
+ case POPT_ARGFLAG_XOR:
+ *arg ^= aLong;
+ break;
+ default:
+ return POPT_ERROR_BADOPERATION;
+ /*@notreached@*/ break;
+ }
+ return 0;
+}
+
+/*@-boundswrite@*/
+/* returns 'val' element, -1 on last item, POPT_ERROR_* on error */
+int poptGetNextOpt(poptContext con)
+{
+ const struct poptOption * opt = NULL;
+ int done = 0;
+
+ if (con == NULL)
+ return -1;
+ while (!done) {
+ const char * origOptString = NULL;
+ poptCallbackType cb = NULL;
+ const void * cbData = NULL;
+ const char * longArg = NULL;
+ int canstrip = 0;
+ int shorty = 0;
+
+ while (!con->os->nextCharArg && con->os->next == con->os->argc
+ && con->os > con->optionStack) {
+ cleanOSE(con->os--);
+ }
+ if (!con->os->nextCharArg && con->os->next == con->os->argc) {
+ /*@-internalglobs@*/
+ invokeCallbacksPOST(con, con->options);
+ /*@=internalglobs@*/
+ if (con->doExec) return execCommand(con);
+ return -1;
+ }
+
+ /* Process next long option */
+ if (!con->os->nextCharArg) {
+ char * localOptString, * optString;
+ int thisopt;
+
+ /*@-sizeoftype@*/
+ if (con->os->argb && PBM_ISSET(con->os->next, con->os->argb)) {
+ con->os->next++;
+ continue;
+ }
+ /*@=sizeoftype@*/
+ thisopt = con->os->next;
+ if (con->os->argv != NULL) /* XXX can't happen */
+ origOptString = con->os->argv[con->os->next++];
+
+ if (origOptString == NULL) /* XXX can't happen */
+ return POPT_ERROR_BADOPT;
+
+ if (con->restLeftover || *origOptString != '-') {
+ if (con->flags & POPT_CONTEXT_POSIXMEHARDER)
+ con->restLeftover = 1;
+ if (con->flags & POPT_CONTEXT_ARG_OPTS) {
+ con->os->nextArg = xstrdup(origOptString);
+ return 0;
+ }
+ if (con->leftovers != NULL) /* XXX can't happen */
+ con->leftovers[con->numLeftovers++] = origOptString;
+ continue;
+ }
+
+ /* Make a copy we can hack at */
+ { size_t bufsize = strlen(origOptString) + 1;
+ localOptString = optString = alloca(bufsize);
+ if (optString == NULL) /* XXX can't happen */
+ return POPT_ERROR_BADOPT;
+ strlcpy(optString, origOptString, bufsize);
+ }
+
+ if (optString[0] == '\0')
+ return POPT_ERROR_BADOPT;
+
+ if (optString[1] == '-' && !optString[2]) {
+ con->restLeftover = 1;
+ continue;
+ } else {
+ char *oe;
+ int singleDash;
+
+ optString++;
+ if (*optString == '-')
+ singleDash = 0, optString++;
+ else
+ singleDash = 1;
+
+ /* XXX aliases with arg substitution need "--alias=arg" */
+ if (handleAlias(con, optString, '\0', NULL))
+ continue;
+
+ if (handleExec(con, optString, '\0'))
+ continue;
+
+ /* Check for "--long=arg" option. */
+ for (oe = optString; *oe && *oe != '='; oe++)
+ {};
+ if (*oe == '=') {
+ *oe++ = '\0';
+ /* XXX longArg is mapped back to persistent storage. */
+ longArg = origOptString + (oe - localOptString);
+ } else
+ oe = NULL;
+
+ opt = findOption(con->options, optString, '\0', &cb, &cbData,
+ singleDash);
+ if (!opt && !singleDash)
+ return POPT_ERROR_BADOPT;
+ if (!opt && oe)
+ oe[-1] = '='; /* restore overwritten '=' */
+ }
+
+ if (!opt) {
+ con->os->nextCharArg = origOptString + 1;
+ longArg = NULL;
+ } else {
+ if (con->os == con->optionStack &&
+ opt->argInfo & POPT_ARGFLAG_STRIP)
+ {
+ canstrip = 1;
+ poptStripArg(con, thisopt);
+ }
+ shorty = 0;
+ }
+ }
+
+ /* Process next short option */
+ /*@-branchstate@*/ /* FIX: W2DO? */
+ if (con->os->nextCharArg) {
+ origOptString = con->os->nextCharArg;
+
+ con->os->nextCharArg = NULL;
+
+ if (handleAlias(con, NULL, *origOptString, origOptString + 1))
+ continue;
+
+ if (handleExec(con, NULL, *origOptString)) {
+ /* Restore rest of short options for further processing */
+ origOptString++;
+ if (*origOptString != '\0')
+ con->os->nextCharArg = origOptString;
+ continue;
+ }
+
+ opt = findOption(con->options, NULL, *origOptString, &cb,
+ &cbData, 0);
+ if (!opt)
+ return POPT_ERROR_BADOPT;
+ shorty = 1;
+
+ origOptString++;
+ if (*origOptString != '\0')
+ con->os->nextCharArg = origOptString;
+ }
+ /*@=branchstate@*/
+
+ if (opt == NULL) return POPT_ERROR_BADOPT; /* XXX can't happen */
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_NONE
+ || (opt->argInfo & POPT_ARG_MASK) == POPT_ARG_VAL) {
+ if (longArg || (con->os->nextCharArg && con->os->nextCharArg[0] == '='))
+ return POPT_ERROR_UNWANTEDARG;
+ if (opt->arg) {
+ long val = (opt->argInfo & POPT_ARG_MASK) == POPT_ARG_VAL ? opt->val : 1;
+ if (poptSaveInt((int *)opt->arg, opt->argInfo, val))
+ return POPT_ERROR_BADOPERATION;
+ }
+ } else {
+ con->os->nextArg = _free(con->os->nextArg);
+ /*@-usedef@*/ /* FIX: W2DO? */
+ if (longArg) {
+ /*@=usedef@*/
+ longArg = expandNextArg(con, longArg);
+ con->os->nextArg = longArg;
+ } else if (con->os->nextCharArg) {
+ longArg = expandNextArg(con, con->os->nextCharArg + (con->os->nextCharArg[0] == '='));
+ con->os->nextArg = longArg;
+ con->os->nextCharArg = NULL;
+ } else {
+ while (con->os->next == con->os->argc &&
+ con->os > con->optionStack) {
+ cleanOSE(con->os--);
+ }
+ if (con->os->next == con->os->argc) {
+ if (!(opt->argInfo & POPT_ARGFLAG_OPTIONAL))
+ /*@-compdef@*/ /* FIX: con->os->argv not defined */
+ return POPT_ERROR_NOARG;
+ /*@=compdef@*/
+ con->os->nextArg = NULL;
+ } else {
+
+ /*
+ * Make sure this isn't part of a short arg or the
+ * result of an alias expansion.
+ */
+ if (con->os == con->optionStack &&
+ (opt->argInfo & POPT_ARGFLAG_STRIP) &&
+ canstrip) {
+ poptStripArg(con, con->os->next);
+ }
+
+ if (con->os->argv != NULL) { /* XXX can't happen */
+ /* XXX watchout: subtle side-effects live here. */
+ longArg = con->os->argv[con->os->next++];
+ longArg = expandNextArg(con, longArg);
+ con->os->nextArg = longArg;
+ }
+ }
+ }
+ longArg = NULL;
+
+ if (opt->arg) {
+ switch (opt->argInfo & POPT_ARG_MASK) {
+ case POPT_ARG_STRING:
+ /* XXX memory leak, hard to plug */
+ *((const char **) opt->arg) = (con->os->nextArg)
+ ? xstrdup(con->os->nextArg) : NULL;
+ /*@switchbreak@*/ break;
+
+ case POPT_ARG_INT:
+ case POPT_ARG_LONG:
+ { long aLong = 0;
+ char *end;
+
+ if (con->os->nextArg) {
+ aLong = strtol(con->os->nextArg, &end, 0);
+ if (!(end && *end == '\0'))
+ return POPT_ERROR_BADNUMBER;
+ }
+
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_LONG) {
+ if (aLong == LONG_MIN || aLong == LONG_MAX)
+ return POPT_ERROR_OVERFLOW;
+ if (poptSaveLong((long *)opt->arg, opt->argInfo, aLong))
+ return POPT_ERROR_BADOPERATION;
+ } else {
+ if (aLong > INT_MAX || aLong < INT_MIN)
+ return POPT_ERROR_OVERFLOW;
+ if (poptSaveInt((int *)opt->arg, opt->argInfo, aLong))
+ return POPT_ERROR_BADOPERATION;
+ }
+ } /*@switchbreak@*/ break;
+
+ case POPT_ARG_FLOAT:
+ case POPT_ARG_DOUBLE:
+ { double aDouble = 0.0;
+ char *end;
+
+ if (con->os->nextArg) {
+ /*@-mods@*/
+ int saveerrno = errno;
+ errno = 0;
+ aDouble = strtod(con->os->nextArg, &end);
+ if (errno == ERANGE)
+ return POPT_ERROR_OVERFLOW;
+ errno = saveerrno;
+ /*@=mods@*/
+ if (*end != '\0')
+ return POPT_ERROR_BADNUMBER;
+ }
+
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_DOUBLE) {
+ *((double *) opt->arg) = aDouble;
+ } else {
+#define MY_ABS(a) ((((a) - 0.0) < DBL_EPSILON) ? -(a) : (a))
+ if ((MY_ABS(aDouble) - FLT_MAX) > DBL_EPSILON)
+ return POPT_ERROR_OVERFLOW;
+ if ((FLT_MIN - MY_ABS(aDouble)) > DBL_EPSILON)
+ return POPT_ERROR_OVERFLOW;
+ *((float *) opt->arg) = aDouble;
+ }
+ } /*@switchbreak@*/ break;
+ default:
+ fprintf(stdout,
+ POPT_("option type (%d) not implemented in popt\n"),
+ (opt->argInfo & POPT_ARG_MASK));
+ exit(EXIT_FAILURE);
+ /*@notreached@*/ /*@switchbreak@*/ break;
+ }
+ }
+ }
+
+ if (cb) {
+ /*@-internalglobs@*/
+ invokeCallbacksOPTION(con, con->options, opt, cbData, shorty);
+ /*@=internalglobs@*/
+ } else if (opt->val && ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL))
+ done = 1;
+
+ if ((con->finalArgvCount + 2) >= (con->finalArgvAlloced)) {
+ con->finalArgvAlloced += 10;
+ con->finalArgv = realloc(con->finalArgv,
+ sizeof(*con->finalArgv) * con->finalArgvAlloced);
+ }
+
+ if (con->finalArgv != NULL)
+ { ssize_t bufsize = (opt->longName ? strlen(opt->longName) : 0) + 3;
+ char *s = malloc(bufsize);
+ if (s != NULL) { /* XXX can't happen */
+ if (opt->longName)
+ snprintf(s, bufsize, "%s%s",
+ ((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "-" : "--"),
+ opt->longName);
+ else
+ snprintf(s, bufsize, "-%c", opt->shortName);
+ con->finalArgv[con->finalArgvCount++] = s;
+ } else
+ con->finalArgv[con->finalArgvCount++] = NULL;
+ }
+
+ if (opt->arg && (opt->argInfo & POPT_ARG_MASK) == POPT_ARG_NONE)
+ /*@-ifempty@*/ ; /*@=ifempty@*/
+ else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_VAL)
+ /*@-ifempty@*/ ; /*@=ifempty@*/
+ else if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE) {
+ if (con->finalArgv != NULL && con->os->nextArg)
+ con->finalArgv[con->finalArgvCount++] =
+ /*@-nullpass@*/ /* LCL: con->os->nextArg != NULL */
+ xstrdup(con->os->nextArg);
+ /*@=nullpass@*/
+ }
+ }
+
+ return (opt ? opt->val : -1); /* XXX can't happen */
+}
+/*@=boundswrite@*/
+
+const char * poptGetOptArg(poptContext con)
+{
+ const char * ret = NULL;
+ /*@-branchstate@*/
+ if (con) {
+ ret = con->os->nextArg;
+ con->os->nextArg = NULL;
+ }
+ /*@=branchstate@*/
+ return ret;
+}
+
+const char * poptGetArg(poptContext con)
+{
+ const char * ret = NULL;
+ if (con && con->leftovers != NULL && con->nextLeftover < con->numLeftovers)
+ ret = con->leftovers[con->nextLeftover++];
+ return ret;
+}
+
+const char * poptPeekArg(poptContext con)
+{
+ const char * ret = NULL;
+ if (con && con->leftovers != NULL && con->nextLeftover < con->numLeftovers)
+ ret = con->leftovers[con->nextLeftover];
+ return ret;
+}
+
+/*@-boundswrite@*/
+const char ** poptGetArgs(poptContext con)
+{
+ if (con == NULL ||
+ con->leftovers == NULL || con->numLeftovers == con->nextLeftover)
+ return NULL;
+
+ /* some apps like [like RPM ;-) ] need this NULL terminated */
+ con->leftovers[con->numLeftovers] = NULL;
+
+ /*@-nullret -nullstate @*/ /* FIX: typedef double indirection. */
+ return (con->leftovers + con->nextLeftover);
+ /*@=nullret =nullstate @*/
+}
+/*@=boundswrite@*/
+
+poptContext poptFreeContext(poptContext con)
+{
+ poptItem item;
+ int i;
+
+ if (con == NULL) return con;
+ poptResetContext(con);
+ con->os->argb = _free(con->os->argb);
+
+ if (con->aliases != NULL)
+ for (i = 0; i < con->numAliases; i++) {
+ item = con->aliases + i;
+ /*@-modobserver -observertrans -dependenttrans@*/
+ item->option.longName = _free(item->option.longName);
+ item->option.descrip = _free(item->option.descrip);
+ item->option.argDescrip = _free(item->option.argDescrip);
+ /*@=modobserver =observertrans =dependenttrans@*/
+ item->argv = _free(item->argv);
+ }
+ con->aliases = _free(con->aliases);
+
+ if (con->execs != NULL)
+ for (i = 0; i < con->numExecs; i++) {
+ item = con->execs + i;
+ /*@-modobserver -observertrans -dependenttrans@*/
+ item->option.longName = _free(item->option.longName);
+ item->option.descrip = _free(item->option.descrip);
+ item->option.argDescrip = _free(item->option.argDescrip);
+ /*@=modobserver =observertrans =dependenttrans@*/
+ item->argv = _free(item->argv);
+ }
+ con->execs = _free(con->execs);
+
+ con->leftovers = _free(con->leftovers);
+ con->finalArgv = _free(con->finalArgv);
+ con->appName = _free(con->appName);
+ con->otherHelp = _free(con->otherHelp);
+ con->execPath = _free(con->execPath);
+ con->arg_strip = PBM_FREE(con->arg_strip);
+
+ con = _free(con);
+ return con;
+}
+
+int poptAddAlias(poptContext con, struct poptAlias alias,
+ /*@unused@*/ UNUSED(int flags))
+{
+ poptItem item = (poptItem) alloca(sizeof(*item));
+ memset(item, 0, sizeof(*item));
+ item->option.longName = alias.longName;
+ item->option.shortName = alias.shortName;
+ item->option.argInfo = POPT_ARGFLAG_DOC_HIDDEN;
+ item->option.arg = 0;
+ item->option.val = 0;
+ item->option.descrip = NULL;
+ item->option.argDescrip = NULL;
+ item->argc = alias.argc;
+ item->argv = alias.argv;
+ return poptAddItem(con, item, 0);
+}
+
+/*@-boundswrite@*/
+/*@-mustmod@*/ /* LCL: con not modified? */
+int poptAddItem(poptContext con, poptItem newItem, int flags)
+{
+ poptItem * items, item;
+ int * nitems;
+
+ switch (flags) {
+ case 1:
+ items = &con->execs;
+ nitems = &con->numExecs;
+ break;
+ case 0:
+ items = &con->aliases;
+ nitems = &con->numAliases;
+ break;
+ default:
+ return 1;
+ /*@notreached@*/ break;
+ }
+
+ *items = realloc((*items), ((*nitems) + 1) * sizeof(**items));
+ if ((*items) == NULL)
+ return 1;
+
+ item = (*items) + (*nitems);
+
+ item->option.longName =
+ (newItem->option.longName ? xstrdup(newItem->option.longName) : NULL);
+ item->option.shortName = newItem->option.shortName;
+ item->option.argInfo = newItem->option.argInfo;
+ item->option.arg = newItem->option.arg;
+ item->option.val = newItem->option.val;
+ item->option.descrip =
+ (newItem->option.descrip ? xstrdup(newItem->option.descrip) : NULL);
+ item->option.argDescrip =
+ (newItem->option.argDescrip ? xstrdup(newItem->option.argDescrip) : NULL);
+ item->argc = newItem->argc;
+ item->argv = newItem->argv;
+
+ (*nitems)++;
+
+ return 0;
+}
+/*@=mustmod@*/
+/*@=boundswrite@*/
+
+const char * poptBadOption(poptContext con, int flags)
+{
+ struct optionStackEntry * os = NULL;
+
+ if (con != NULL)
+ os = (flags & POPT_BADOPTION_NOALIAS) ? con->optionStack : con->os;
+
+ /*@-nullderef@*/ /* LCL: os->argv != NULL */
+ return (os && os->argv ? os->argv[os->next - 1] : NULL);
+ /*@=nullderef@*/
+}
+
+const char * poptStrerror(const int error)
+{
+ switch (error) {
+ case POPT_ERROR_NOARG:
+ return POPT_("missing argument");
+ case POPT_ERROR_UNWANTEDARG:
+ return POPT_("option does not take an argument");
+ case POPT_ERROR_BADOPT:
+ return POPT_("unknown option");
+ case POPT_ERROR_BADOPERATION:
+ return POPT_("mutually exclusive logical operations requested");
+ case POPT_ERROR_NULLARG:
+ return POPT_("opt->arg should not be NULL");
+ case POPT_ERROR_OPTSTOODEEP:
+ return POPT_("aliases nested too deeply");
+ case POPT_ERROR_BADQUOTE:
+ return POPT_("error in parameter quoting");
+ case POPT_ERROR_BADNUMBER:
+ return POPT_("invalid numeric value");
+ case POPT_ERROR_OVERFLOW:
+ return POPT_("number too large or too small");
+ case POPT_ERROR_MALLOC:
+ return POPT_("memory allocation failed");
+ case POPT_ERROR_ERRNO:
+ return strerror(errno);
+ default:
+ return POPT_("unknown error");
+ }
+}
+
+int poptStuffArgs(poptContext con, const char ** argv)
+{
+ int argc;
+ int rc;
+
+ if ((con->os - con->optionStack) == POPT_OPTION_DEPTH)
+ return POPT_ERROR_OPTSTOODEEP;
+
+ for (argc = 0; argv[argc]; argc++)
+ {};
+
+ con->os++;
+ con->os->next = 0;
+ con->os->nextArg = NULL;
+ con->os->nextCharArg = NULL;
+ con->os->currAlias = NULL;
+ rc = poptDupArgv(argc, argv, &con->os->argc, &con->os->argv);
+ con->os->argb = NULL;
+ con->os->stuffed = 1;
+
+ return rc;
+}
+
+const char * poptGetInvocationName(poptContext con)
+{
+ return (con->os->argv ? con->os->argv[0] : "");
+}
+
+/*@-boundswrite@*/
+int poptStrippedArgv(poptContext con, int argc, char ** argv)
+{
+ int numargs = argc;
+ int j = 1;
+ int i;
+
+ /*@-sizeoftype@*/
+ if (con->arg_strip)
+ for (i = 1; i < argc; i++) {
+ if (PBM_ISSET(i, con->arg_strip))
+ numargs--;
+ }
+
+ for (i = 1; i < argc; i++) {
+ if (con->arg_strip && PBM_ISSET(i, con->arg_strip))
+ continue;
+ argv[j] = (j < numargs) ? argv[i] : NULL;
+ j++;
+ }
+ /*@=sizeoftype@*/
+
+ return numargs;
+}
+/*@=boundswrite@*/
diff --git a/rsync/popt/popt.h b/rsync/popt/popt.h
new file mode 100644
index 0000000..8d85f73
--- /dev/null
+++ b/rsync/popt/popt.h
@@ -0,0 +1,565 @@
+/** \file popt/popt.h
+ * \ingroup popt
+ */
+
+/* (C) 1998-2000 Red Hat, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.rpm.org/pub/rpm/dist. */
+
+#ifndef H_POPT
+#define H_POPT
+
+#include /* for FILE * */
+
+#define POPT_OPTION_DEPTH 10
+
+/** \ingroup popt
+ * \name Arg type identifiers
+ */
+/*@{*/
+#define POPT_ARG_NONE 0 /*!< no arg */
+#define POPT_ARG_STRING 1 /*!< arg will be saved as string */
+#define POPT_ARG_INT 2 /*!< arg will be converted to int */
+#define POPT_ARG_LONG 3 /*!< arg will be converted to long */
+#define POPT_ARG_INCLUDE_TABLE 4 /*!< arg points to table */
+#define POPT_ARG_CALLBACK 5 /*!< table-wide callback... must be
+ set first in table; arg points
+ to callback, descrip points to
+ callback data to pass */
+#define POPT_ARG_INTL_DOMAIN 6 /*!< set the translation domain
+ for this table and any
+ included tables; arg points
+ to the domain string */
+#define POPT_ARG_VAL 7 /*!< arg should take value val */
+#define POPT_ARG_FLOAT 8 /*!< arg will be converted to float */
+#define POPT_ARG_DOUBLE 9 /*!< arg will be converted to double */
+
+#define POPT_ARG_MASK 0x0000FFFF
+/*@}*/
+
+/** \ingroup popt
+ * \name Arg modifiers
+ */
+/*@{*/
+#define POPT_ARGFLAG_ONEDASH 0x80000000 /*!< allow -longoption */
+#define POPT_ARGFLAG_DOC_HIDDEN 0x40000000 /*!< don't show in help/usage */
+#define POPT_ARGFLAG_STRIP 0x20000000 /*!< strip this arg from argv(only applies to long args) */
+#define POPT_ARGFLAG_OPTIONAL 0x10000000 /*!< arg may be missing */
+
+#define POPT_ARGFLAG_OR 0x08000000 /*!< arg will be or'ed */
+#define POPT_ARGFLAG_NOR 0x09000000 /*!< arg will be nor'ed */
+#define POPT_ARGFLAG_AND 0x04000000 /*!< arg will be and'ed */
+#define POPT_ARGFLAG_NAND 0x05000000 /*!< arg will be nand'ed */
+#define POPT_ARGFLAG_XOR 0x02000000 /*!< arg will be xor'ed */
+#define POPT_ARGFLAG_NOT 0x01000000 /*!< arg will be negated */
+#define POPT_ARGFLAG_LOGICALOPS \
+ (POPT_ARGFLAG_OR|POPT_ARGFLAG_AND|POPT_ARGFLAG_XOR)
+
+#define POPT_BIT_SET (POPT_ARG_VAL|POPT_ARGFLAG_OR)
+ /*!< set arg bit(s) */
+#define POPT_BIT_CLR (POPT_ARG_VAL|POPT_ARGFLAG_NAND)
+ /*!< clear arg bit(s) */
+
+#define POPT_ARGFLAG_SHOW_DEFAULT 0x00800000 /*!< show default value in --help */
+
+/*@}*/
+
+/** \ingroup popt
+ * \name Callback modifiers
+ */
+/*@{*/
+#define POPT_CBFLAG_PRE 0x80000000 /*!< call the callback before parse */
+#define POPT_CBFLAG_POST 0x40000000 /*!< call the callback after parse */
+#define POPT_CBFLAG_INC_DATA 0x20000000 /*!< use data from the include line,
+ not the subtable */
+#define POPT_CBFLAG_SKIPOPTION 0x10000000 /*!< don't callback with option */
+#define POPT_CBFLAG_CONTINUE 0x08000000 /*!< continue callbacks with option */
+/*@}*/
+
+/** \ingroup popt
+ * \name Error return values
+ */
+/*@{*/
+#define POPT_ERROR_NOARG -10 /*!< missing argument */
+#define POPT_ERROR_BADOPT -11 /*!< unknown option */
+#define POPT_ERROR_UNWANTEDARG -12 /*!< option does not take an argument */
+#define POPT_ERROR_OPTSTOODEEP -13 /*!< aliases nested too deeply */
+#define POPT_ERROR_BADQUOTE -15 /*!< error in paramter quoting */
+#define POPT_ERROR_ERRNO -16 /*!< errno set, use strerror(errno) */
+#define POPT_ERROR_BADNUMBER -17 /*!< invalid numeric value */
+#define POPT_ERROR_OVERFLOW -18 /*!< number too large or too small */
+#define POPT_ERROR_BADOPERATION -19 /*!< mutually exclusive logical operations requested */
+#define POPT_ERROR_NULLARG -20 /*!< opt->arg should not be NULL */
+#define POPT_ERROR_MALLOC -21 /*!< memory allocation failed */
+/*@}*/
+
+/** \ingroup popt
+ * \name poptBadOption() flags
+ */
+/*@{*/
+#define POPT_BADOPTION_NOALIAS (1 << 0) /*!< don't go into an alias */
+/*@}*/
+
+/** \ingroup popt
+ * \name poptGetContext() flags
+ */
+/*@{*/
+#define POPT_CONTEXT_NO_EXEC (1 << 0) /*!< ignore exec expansions */
+#define POPT_CONTEXT_KEEP_FIRST (1 << 1) /*!< pay attention to argv[0] */
+#define POPT_CONTEXT_POSIXMEHARDER (1 << 2) /*!< options can't follow args */
+#define POPT_CONTEXT_ARG_OPTS (1 << 4) /*!< return args as options with value 0 */
+/*@}*/
+
+/** \ingroup popt
+ */
+struct poptOption {
+/*@observer@*/ /*@null@*/
+ const char * longName; /*!< may be NULL */
+ char shortName; /*!< may be NUL */
+ int argInfo;
+/*@shared@*/ /*@null@*/
+ void * arg; /*!< depends on argInfo */
+ int val; /*!< 0 means don't return, just update flag */
+/*@observer@*/ /*@null@*/
+ const char * descrip; /*!< description for autohelp -- may be NULL */
+/*@observer@*/ /*@null@*/
+ const char * argDescrip; /*!< argument description for autohelp */
+};
+
+/** \ingroup popt
+ * A popt alias argument for poptAddAlias().
+ */
+struct poptAlias {
+/*@owned@*/ /*@null@*/
+ const char * longName; /*!< may be NULL */
+ char shortName; /*!< may be NUL */
+ int argc;
+/*@owned@*/
+ const char ** argv; /*!< must be free()able */
+};
+
+/** \ingroup popt
+ * A popt alias or exec argument for poptAddItem().
+ */
+/*@-exporttype@*/
+typedef struct poptItem_s {
+ struct poptOption option; /*!< alias/exec name(s) and description. */
+ int argc; /*!< (alias) no. of args. */
+/*@owned@*/
+ const char ** argv; /*!< (alias) args, must be free()able. */
+} * poptItem;
+/*@=exporttype@*/
+
+/** \ingroup popt
+ * \name Auto-generated help/usage
+ */
+/*@{*/
+
+/**
+ * Empty table marker to enable displaying popt alias/exec options.
+ */
+/*@-exportvar@*/
+/*@unchecked@*/ /*@observer@*/
+extern struct poptOption poptAliasOptions[];
+/*@=exportvar@*/
+#define POPT_AUTOALIAS { NULL, '\0', POPT_ARG_INCLUDE_TABLE, poptAliasOptions, \
+ 0, "Options implemented via popt alias/exec:", NULL },
+
+/**
+ * Auto help table options.
+ */
+/*@-exportvar@*/
+/*@unchecked@*/ /*@observer@*/
+extern struct poptOption poptHelpOptions[];
+/*@=exportvar@*/
+
+/*@-exportvar@*/
+/*@unchecked@*/ /*@observer@*/
+extern struct poptOption * poptHelpOptionsI18N;
+/*@=exportvar@*/
+
+#define POPT_AUTOHELP { NULL, '\0', POPT_ARG_INCLUDE_TABLE, poptHelpOptions, \
+ 0, "Help options:", NULL },
+
+#define POPT_TABLEEND { NULL, '\0', 0, 0, 0, NULL, NULL }
+/*@}*/
+
+/** \ingroup popt
+ */
+/*@-exporttype@*/
+typedef /*@abstract@*/ struct poptContext_s * poptContext;
+/*@=exporttype@*/
+
+/** \ingroup popt
+ */
+#ifndef __cplusplus
+/*@-exporttype -typeuse@*/
+typedef struct poptOption * poptOption;
+/*@=exporttype =typeuse@*/
+#endif
+
+/*@-exportconst@*/
+enum poptCallbackReason {
+ POPT_CALLBACK_REASON_PRE = 0,
+ POPT_CALLBACK_REASON_POST = 1,
+ POPT_CALLBACK_REASON_OPTION = 2
+};
+/*@=exportconst@*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*@-type@*/
+
+/** \ingroup popt
+ * Table callback prototype.
+ * @param con context
+ * @param reason reason for callback
+ * @param opt option that triggered callback
+ * @param arg @todo Document.
+ * @param data @todo Document.
+ */
+typedef void (*poptCallbackType) (poptContext con,
+ enum poptCallbackReason reason,
+ /*@null@*/ const struct poptOption * opt,
+ /*@null@*/ const char * arg,
+ /*@null@*/ const void * data)
+ /*@globals internalState @*/
+ /*@modifies internalState @*/;
+
+/** \ingroup popt
+ * Initialize popt context.
+ * @param name context name (usually argv[0] program name)
+ * @param argc no. of arguments
+ * @param argv argument array
+ * @param options address of popt option table
+ * @param flags or'd POPT_CONTEXT_* bits
+ * @return initialized popt context
+ */
+/*@only@*/ /*@null@*/
+poptContext poptGetContext(
+ /*@dependent@*/ /*@keep@*/ const char * name,
+ int argc, /*@dependent@*/ /*@keep@*/ const char ** argv,
+ /*@dependent@*/ /*@keep@*/ const struct poptOption * options,
+ int flags)
+ /*@*/;
+
+/** \ingroup popt
+ * Reinitialize popt context.
+ * @param con context
+ */
+/*@unused@*/
+void poptResetContext(/*@null@*/poptContext con)
+ /*@modifies con @*/;
+
+/** \ingroup popt
+ * Return value of next option found.
+ * @param con context
+ * @return next option val, -1 on last item, POPT_ERROR_* on error
+ */
+int poptGetNextOpt(/*@null@*/poptContext con)
+ /*@globals fileSystem, internalState @*/
+ /*@modifies con, fileSystem, internalState @*/;
+
+/** \ingroup popt
+ * Return next option argument (if any).
+ * @param con context
+ * @return option argument, NULL if no argument is available
+ */
+/*@observer@*/ /*@null@*/ /*@unused@*/
+const char * poptGetOptArg(/*@null@*/poptContext con)
+ /*@modifies con @*/;
+
+/** \ingroup popt
+ * Return next argument.
+ * @param con context
+ * @return next argument, NULL if no argument is available
+ */
+/*@observer@*/ /*@null@*/ /*@unused@*/
+const char * poptGetArg(/*@null@*/poptContext con)
+ /*@modifies con @*/;
+
+/** \ingroup popt
+ * Peek at current argument.
+ * @param con context
+ * @return current argument, NULL if no argument is available
+ */
+/*@observer@*/ /*@null@*/ /*@unused@*/
+const char * poptPeekArg(/*@null@*/poptContext con)
+ /*@*/;
+
+/** \ingroup popt
+ * Return remaining arguments.
+ * @param con context
+ * @return argument array, NULL terminated
+ */
+/*@observer@*/ /*@null@*/
+const char ** poptGetArgs(/*@null@*/poptContext con)
+ /*@modifies con @*/;
+
+/** \ingroup popt
+ * Return the option which caused the most recent error.
+ * @param con context
+ * @param flags
+ * @return offending option
+ */
+/*@observer@*/
+const char * poptBadOption(/*@null@*/poptContext con, int flags)
+ /*@*/;
+
+/** \ingroup popt
+ * Destroy context.
+ * @param con context
+ * @return NULL always
+ */
+/*@null@*/
+poptContext poptFreeContext( /*@only@*/ /*@null@*/ poptContext con)
+ /*@modifies con @*/;
+
+/** \ingroup popt
+ * Add arguments to context.
+ * @param con context
+ * @param argv argument array, NULL terminated
+ * @return 0 on success, POPT_ERROR_OPTSTOODEEP on failure
+ */
+/*@unused@*/
+int poptStuffArgs(poptContext con, /*@keep@*/ const char ** argv)
+ /*@modifies con @*/;
+
+/** \ingroup popt
+ * Add alias to context.
+ * @todo Pass alias by reference, not value.
+ * @deprecated Use poptAddItem instead.
+ * @param con context
+ * @param alias alias to add
+ * @param flags (unused)
+ * @return 0 on success
+ */
+/*@unused@*/
+int poptAddAlias(poptContext con, struct poptAlias alias, int flags)
+ /*@modifies con @*/;
+
+/** \ingroup popt
+ * Add alias/exec item to context.
+ * @param con context
+ * @param newItem alias/exec item to add
+ * @param flags 0 for alias, 1 for exec
+ * @return 0 on success
+ */
+int poptAddItem(poptContext con, poptItem newItem, int flags)
+ /*@modifies con @*/;
+
+/** \ingroup popt
+ * Read configuration file.
+ * @param con context
+ * @param fn file name to read
+ * @return 0 on success, POPT_ERROR_ERRNO on failure
+ */
+int poptReadConfigFile(poptContext con, const char * fn)
+ /*@globals errno, fileSystem, internalState @*/
+ /*@modifies con->execs, con->numExecs,
+ errno, fileSystem, internalState @*/;
+
+/** \ingroup popt
+ * Read default configuration from /etc/popt and $HOME/.popt.
+ * @param con context
+ * @param useEnv (unused)
+ * @return 0 on success, POPT_ERROR_ERRNO on failure
+ */
+/*@unused@*/
+int poptReadDefaultConfig(poptContext con, /*@unused@*/ int useEnv)
+ /*@globals fileSystem, internalState @*/
+ /*@modifies con->execs, con->numExecs,
+ fileSystem, internalState @*/;
+
+/** \ingroup popt
+ * Duplicate an argument array.
+ * @note: The argument array is malloc'd as a single area, so only argv must
+ * be free'd.
+ *
+ * @param argc no. of arguments
+ * @param argv argument array
+ * @retval argcPtr address of returned no. of arguments
+ * @retval argvPtr address of returned argument array
+ * @return 0 on success, POPT_ERROR_NOARG on failure
+ */
+int poptDupArgv(int argc, /*@null@*/ const char **argv,
+ /*@null@*/ /*@out@*/ int * argcPtr,
+ /*@null@*/ /*@out@*/ const char *** argvPtr)
+ /*@modifies *argcPtr, *argvPtr @*/;
+
+/** \ingroup popt
+ * Parse a string into an argument array.
+ * The parse allows ', ", and \ quoting, but ' is treated the same as " and
+ * both may include \ quotes.
+ * @note: The argument array is malloc'd as a single area, so only argv must
+ * be free'd.
+ *
+ * @param s string to parse
+ * @retval argcPtr address of returned no. of arguments
+ * @retval argvPtr address of returned argument array
+ */
+int poptParseArgvString(const char * s,
+ /*@out@*/ int * argcPtr, /*@out@*/ const char *** argvPtr)
+ /*@modifies *argcPtr, *argvPtr @*/;
+
+/** \ingroup popt
+ * Parses an input configuration file and returns an string that is a
+ * command line. For use with popt. You must free the return value when done.
+ *
+ * Given the file:
+\verbatim
+# this line is ignored
+ # this one too
+aaa
+ bbb
+ ccc
+bla=bla
+
+this_is = fdsafdas
+ bad_line=
+ reall bad line
+ reall bad line = again
+5555= 55555
+ test = with lots of spaces
+\endverbatim
+*
+* The result is:
+\verbatim
+--aaa --bbb --ccc --bla="bla" --this_is="fdsafdas" --5555="55555" --test="with lots of spaces"
+\endverbatim
+*
+* Passing this to poptParseArgvString() yields an argv of:
+\verbatim
+'--aaa'
+'--bbb'
+'--ccc'
+'--bla=bla'
+'--this_is=fdsafdas'
+'--5555=55555'
+'--test=with lots of spaces'
+\endverbatim
+ *
+ * @bug NULL is returned if file line is too long.
+ * @bug Silently ignores invalid lines.
+ *
+ * @param fp file handle to read
+ * @param *argstrp return string of options (malloc'd)
+ * @param flags unused
+ * @return 0 on success
+ * @see poptParseArgvString
+ */
+/*@-fcnuse@*/
+int poptConfigFileToString(FILE *fp, /*@out@*/ char ** argstrp, int flags)
+ /*@globals fileSystem @*/
+ /*@modifies *fp, *argstrp, fileSystem @*/;
+/*@=fcnuse@*/
+
+/** \ingroup popt
+ * Return formatted error string for popt failure.
+ * @param error popt error
+ * @return error string
+ */
+/*@observer@*/
+const char * poptStrerror(const int error)
+ /*@*/;
+
+/** \ingroup popt
+ * Limit search for executables.
+ * @param con context
+ * @param path single path to search for executables
+ * @param allowAbsolute absolute paths only?
+ */
+/*@unused@*/
+void poptSetExecPath(poptContext con, const char * path, int allowAbsolute)
+ /*@modifies con @*/;
+
+/** \ingroup popt
+ * Print detailed description of options.
+ * @param con context
+ * @param fp ouput file handle
+ * @param flags (unused)
+ */
+void poptPrintHelp(poptContext con, FILE * fp, /*@unused@*/ int flags)
+ /*@globals fileSystem @*/
+ /*@modifies *fp, fileSystem @*/;
+
+/** \ingroup popt
+ * Print terse description of options.
+ * @param con context
+ * @param fp ouput file handle
+ * @param flags (unused)
+ */
+void poptPrintUsage(poptContext con, FILE * fp, /*@unused@*/ int flags)
+ /*@globals fileSystem @*/
+ /*@modifies *fp, fileSystem @*/;
+
+/** \ingroup popt
+ * Provide text to replace default "[OPTION...]" in help/usage output.
+ * @param con context
+ * @param text replacement text
+ */
+/*@-fcnuse@*/
+void poptSetOtherOptionHelp(poptContext con, const char * text)
+ /*@modifies con @*/;
+/*@=fcnuse@*/
+
+/** \ingroup popt
+ * Return argv[0] from context.
+ * @param con context
+ * @return argv[0]
+ */
+/*@-fcnuse@*/
+/*@observer@*/
+const char * poptGetInvocationName(poptContext con)
+ /*@*/;
+/*@=fcnuse@*/
+
+/** \ingroup popt
+ * Shuffle argv pointers to remove stripped args, returns new argc.
+ * @param con context
+ * @param argc no. of args
+ * @param argv arg vector
+ * @return new argc
+ */
+/*@-fcnuse@*/
+int poptStrippedArgv(poptContext con, int argc, char ** argv)
+ /*@modifies *argv @*/;
+/*@=fcnuse@*/
+
+/**
+ * Save a long, performing logical operation with value.
+ * @warning Alignment check may be too strict on certain platorms.
+ * @param arg integer pointer, aligned on int boundary.
+ * @param argInfo logical operation (see POPT_ARGFLAG_*)
+ * @param aLong value to use
+ * @return 0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
+ */
+/*@-incondefs@*/
+/*@unused@*/
+int poptSaveLong(/*@null@*/ long * arg, int argInfo, long aLong)
+ /*@modifies *arg @*/
+ /*@requires maxSet(arg) >= 0 /\ maxRead(arg) == 0 @*/;
+/*@=incondefs@*/
+
+/**
+ * Save an integer, performing logical operation with value.
+ * @warning Alignment check may be too strict on certain platorms.
+ * @param arg integer pointer, aligned on int boundary.
+ * @param argInfo logical operation (see POPT_ARGFLAG_*)
+ * @param aLong value to use
+ * @return 0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
+ */
+/*@-incondefs@*/
+/*@unused@*/
+int poptSaveInt(/*@null@*/ int * arg, int argInfo, long aLong)
+ /*@modifies *arg @*/
+ /*@requires maxSet(arg) >= 0 /\ maxRead(arg) == 0 @*/;
+/*@=incondefs@*/
+
+/*@=type@*/
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/rsync/popt/poptconfig.c b/rsync/popt/poptconfig.c
new file mode 100644
index 0000000..9733d15
--- /dev/null
+++ b/rsync/popt/poptconfig.c
@@ -0,0 +1,183 @@
+/** \ingroup popt
+ * \file popt/poptconfig.c
+ */
+
+/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.rpm.org/pub/rpm/dist. */
+
+#include "system.h"
+#include "poptint.h"
+/*@access poptContext @*/
+
+/*@-compmempass@*/ /* FIX: item->option.longName kept, not dependent. */
+static void configLine(poptContext con, char * line)
+ /*@modifies con @*/
+{
+ size_t nameLength;
+ const char * entryType;
+ const char * opt;
+ poptItem item = (poptItem) alloca(sizeof(*item));
+ int i, j;
+
+ if (con->appName == NULL)
+ return;
+ nameLength = strlen(con->appName);
+
+/*@-boundswrite@*/
+ memset(item, 0, sizeof(*item));
+
+ if (strncmp(line, con->appName, nameLength)) return;
+
+ line += nameLength;
+ if (*line == '\0' || !isSpace(line)) return;
+
+ while (*line != '\0' && isSpace(line)) line++;
+ entryType = line;
+ while (*line == '\0' || !isSpace(line)) line++;
+ *line++ = '\0';
+
+ while (*line != '\0' && isSpace(line)) line++;
+ if (*line == '\0') return;
+ opt = line;
+ while (*line == '\0' || !isSpace(line)) line++;
+ *line++ = '\0';
+
+ while (*line != '\0' && isSpace(line)) line++;
+ if (*line == '\0') return;
+
+ /*@-temptrans@*/ /* FIX: line alias is saved */
+ if (opt[0] == '-' && opt[1] == '-')
+ item->option.longName = opt + 2;
+ else if (opt[0] == '-' && opt[2] == '\0')
+ item->option.shortName = opt[1];
+ /*@=temptrans@*/
+
+ if (poptParseArgvString(line, &item->argc, &item->argv)) return;
+
+ /*@-modobserver@*/
+ item->option.argInfo = POPT_ARGFLAG_DOC_HIDDEN;
+ for (i = 0, j = 0; i < item->argc; i++, j++) {
+ const char * f;
+ if (!strncmp(item->argv[i], "--POPTdesc=", sizeof("--POPTdesc=")-1)) {
+ f = item->argv[i] + sizeof("--POPTdesc=");
+ if (f[0] == '$' && f[1] == '"') f++;
+ item->option.descrip = f;
+ item->option.argInfo &= ~POPT_ARGFLAG_DOC_HIDDEN;
+ j--;
+ } else
+ if (!strncmp(item->argv[i], "--POPTargs=", sizeof("--POPTargs=")-1)) {
+ f = item->argv[i] + sizeof("--POPTargs=");
+ if (f[0] == '$' && f[1] == '"') f++;
+ item->option.argDescrip = f;
+ item->option.argInfo &= ~POPT_ARGFLAG_DOC_HIDDEN;
+ item->option.argInfo |= POPT_ARG_STRING;
+ j--;
+ } else
+ if (j != i)
+ item->argv[j] = item->argv[i];
+ }
+ if (j != i) {
+ item->argv[j] = NULL;
+ item->argc = j;
+ }
+ /*@=modobserver@*/
+/*@=boundswrite@*/
+
+ /*@-nullstate@*/ /* FIX: item->argv[] may be NULL */
+ if (!strcmp(entryType, "alias"))
+ (void) poptAddItem(con, item, 0);
+ else if (!strcmp(entryType, "exec"))
+ (void) poptAddItem(con, item, 1);
+ /*@=nullstate@*/
+}
+/*@=compmempass@*/
+
+int poptReadConfigFile(poptContext con, const char * fn)
+{
+ const char * file, * chptr, * end;
+ char * buf;
+/*@dependent@*/ char * dst;
+ int fd, rc;
+ off_t fileLength;
+
+ fd = open(fn, O_RDONLY);
+ if (fd < 0)
+ return (errno == ENOENT ? 0 : POPT_ERROR_ERRNO);
+
+ fileLength = lseek(fd, 0, SEEK_END);
+ if (fileLength == -1 || lseek(fd, 0, 0) == -1) {
+ rc = errno;
+ (void) close(fd);
+ errno = rc;
+ return POPT_ERROR_ERRNO;
+ }
+
+ file = alloca(fileLength + 1);
+ if (read(fd, (char *)file, fileLength) != fileLength) {
+ rc = errno;
+ (void) close(fd);
+ errno = rc;
+ return POPT_ERROR_ERRNO;
+ }
+ if (close(fd) == -1)
+ return POPT_ERROR_ERRNO;
+
+/*@-boundswrite@*/
+ dst = buf = alloca(fileLength + 1);
+
+ chptr = file;
+ end = (file + fileLength);
+ /*@-infloops@*/ /* LCL: can't detect chptr++ */
+ while (chptr < end) {
+ switch (*chptr) {
+ case '\n':
+ *dst = '\0';
+ dst = buf;
+ while (*dst && isSpace(dst)) dst++;
+ if (*dst && *dst != '#')
+ configLine(con, dst);
+ chptr++;
+ /*@switchbreak@*/ break;
+ case '\\':
+ *dst++ = *chptr++;
+ if (chptr < end) {
+ if (*chptr == '\n')
+ dst--, chptr++;
+ /* \ at the end of a line does not insert a \n */
+ else
+ *dst++ = *chptr++;
+ }
+ /*@switchbreak@*/ break;
+ default:
+ *dst++ = *chptr++;
+ /*@switchbreak@*/ break;
+ }
+ }
+ /*@=infloops@*/
+/*@=boundswrite@*/
+
+ return 0;
+}
+
+int poptReadDefaultConfig(poptContext con, /*@unused@*/ UNUSED(int useEnv))
+{
+ char * fn, * home;
+ int rc;
+
+ if (con->appName == NULL) return 0;
+
+ rc = poptReadConfigFile(con, "/etc/popt");
+ if (rc) return rc;
+
+ if ((home = getenv("HOME"))) {
+ size_t bufsize = strlen(home) + 20;
+ fn = alloca(bufsize);
+ if (fn == NULL) return 0;
+ snprintf(fn, bufsize, "%s/.popt", home);
+ rc = poptReadConfigFile(con, fn);
+ if (rc) return rc;
+ }
+
+ return 0;
+}
diff --git a/rsync/popt/popthelp.c b/rsync/popt/popthelp.c
new file mode 100644
index 0000000..6a00976
--- /dev/null
+++ b/rsync/popt/popthelp.c
@@ -0,0 +1,826 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/** \ingroup popt
+ * \file popt/popthelp.c
+ */
+
+/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.rpm.org/pub/rpm/dist. */
+
+#include "system.h"
+
+/*#define POPT_WCHAR_HACK*/
+#ifdef POPT_WCHAR_HACK
+#include /* for mbsrtowcs */
+/*@access mbstate_t @*/
+#endif
+#include "poptint.h"
+
+/*@access poptContext@*/
+
+/**
+ * Display arguments.
+ * @param con context
+ * @param foo (unused)
+ * @param key option(s)
+ * @param arg (unused)
+ * @param data (unused)
+ */
+static void displayArgs(poptContext con,
+ /*@unused@*/ UNUSED(enum poptCallbackReason foo),
+ struct poptOption * key,
+ /*@unused@*/ UNUSED(const char * arg), /*@unused@*/ UNUSED(void * data))
+ /*@globals fileSystem@*/
+ /*@modifies fileSystem@*/
+{
+ if (key->shortName == '?')
+ poptPrintHelp(con, stdout, 0);
+ else
+ poptPrintUsage(con, stdout, 0);
+ exit(0);
+}
+
+#ifdef NOTYET
+/*@unchecked@*/
+static int show_option_defaults = 0;
+#endif
+
+/**
+ * Empty table marker to enable displaying popt alias/exec options.
+ */
+/*@observer@*/ /*@unchecked@*/
+struct poptOption poptAliasOptions[] = {
+ POPT_TABLEEND
+};
+
+/**
+ * Auto help table options.
+ */
+/*@-castfcnptr@*/
+/*@observer@*/ /*@unchecked@*/
+struct poptOption poptHelpOptions[] = {
+ { NULL, '\0', POPT_ARG_CALLBACK, (void *)&displayArgs, '\0', NULL, NULL },
+ { "help", '?', 0, NULL, '?', N_("Show this help message"), NULL },
+ { "usage", '\0', 0, NULL, 'u', N_("Display brief usage message"), NULL },
+ POPT_TABLEEND
+} ;
+
+/*@observer@*/ /*@unchecked@*/
+static struct poptOption poptHelpOptions2[] = {
+/*@-readonlytrans@*/
+ { NULL, '\0', POPT_ARG_INTL_DOMAIN, PACKAGE, 0, NULL, NULL},
+/*@=readonlytrans@*/
+ { NULL, '\0', POPT_ARG_CALLBACK, (void *)&displayArgs, '\0', NULL, NULL },
+ { "help", '?', 0, NULL, '?', N_("Show this help message"), NULL },
+ { "usage", '\0', 0, NULL, 'u', N_("Display brief usage message"), NULL },
+#ifdef NOTYET
+ { "defaults", '\0', POPT_ARG_NONE, &show_option_defaults, 0,
+ N_("Display option defaults in message"), NULL },
+#endif
+ POPT_TABLEEND
+} ;
+
+/*@observer@*/ /*@unchecked@*/
+struct poptOption * poptHelpOptionsI18N = poptHelpOptions2;
+/*@=castfcnptr@*/
+
+/**
+ * @param table option(s)
+ */
+/*@observer@*/ /*@null@*/ static const char *
+getTableTranslationDomain(/*@null@*/ const struct poptOption *table)
+ /*@*/
+{
+ const struct poptOption *opt;
+
+ if (table != NULL)
+ for (opt = table; opt->longName || opt->shortName || opt->arg; opt++) {
+ if (opt->argInfo == POPT_ARG_INTL_DOMAIN)
+ return opt->arg;
+ }
+ return NULL;
+}
+
+/**
+ * @param opt option(s)
+ * @param translation_domain translation domain
+ */
+/*@observer@*/ /*@null@*/ static const char *
+getArgDescrip(const struct poptOption * opt,
+ /*@-paramuse@*/ /* FIX: i18n macros disabled with lclint */
+ /*@null@*/ UNUSED(const char * translation_domain))
+ /*@=paramuse@*/
+ /*@*/
+{
+ if (!(opt->argInfo & POPT_ARG_MASK)) return NULL;
+
+ if (opt == (poptHelpOptions + 1) || opt == (poptHelpOptions + 2))
+ if (opt->argDescrip) return POPT_(opt->argDescrip);
+
+ if (opt->argDescrip) return D_(translation_domain, opt->argDescrip);
+
+ switch (opt->argInfo & POPT_ARG_MASK) {
+ /*case POPT_ARG_NONE: return POPT_("NONE");*/ /* impossible */
+#ifdef DYING
+ case POPT_ARG_VAL: return POPT_("VAL");
+#else
+ case POPT_ARG_VAL: return NULL;
+#endif
+ case POPT_ARG_INT: return POPT_("INT");
+ case POPT_ARG_LONG: return POPT_("LONG");
+ case POPT_ARG_STRING: return POPT_("STRING");
+ case POPT_ARG_FLOAT: return POPT_("FLOAT");
+ case POPT_ARG_DOUBLE: return POPT_("DOUBLE");
+ default: return POPT_("ARG");
+ }
+}
+
+/**
+ * Display default value for an option.
+ * @param lineLength display positions remaining
+ * @param opt option(s)
+ * @param translation_domain translation domain
+ * @return
+ */
+static /*@only@*/ /*@null@*/ char *
+singleOptionDefaultValue(size_t lineLength,
+ const struct poptOption * opt,
+ /*@-paramuse@*/ /* FIX: i18n macros disabled with lclint */
+ /*@null@*/ UNUSED(const char * translation_domain))
+ /*@=paramuse@*/
+ /*@*/
+{
+ const char * defstr = D_(translation_domain, "default");
+ size_t limit, bufsize = 4*lineLength + 1;
+ char * le = malloc(bufsize);
+ char * l = le;
+
+ if (le == NULL) return NULL; /* XXX can't happen */
+/*@-boundswrite@*/
+ *le++ = '(';
+ le += strlcpy(le, defstr, bufsize - 3);
+ *le++ = ':';
+ *le++ = ' ';
+ limit = bufsize - (le - l) - 1; /* -1 for closing paren */
+ if (opt->arg) /* XXX programmer error */
+ switch (opt->argInfo & POPT_ARG_MASK) {
+ case POPT_ARG_VAL:
+ case POPT_ARG_INT:
+ { long aLong = *((int *)opt->arg);
+ le += snprintf(le, limit, "%ld", aLong);
+ } break;
+ case POPT_ARG_LONG:
+ { long aLong = *((long *)opt->arg);
+ le += snprintf(le, limit, "%ld", aLong);
+ } break;
+ case POPT_ARG_FLOAT:
+ { double aDouble = *((float *)opt->arg);
+ le += snprintf(le, limit, "%g", aDouble);
+ } break;
+ case POPT_ARG_DOUBLE:
+ { double aDouble = *((double *)opt->arg);
+ le += snprintf(le, limit, "%g", aDouble);
+ } break;
+ case POPT_ARG_STRING:
+ { const char * s = *(const char **)opt->arg;
+ if (s == NULL) {
+ le += strlcpy(le, "null", limit);
+ } else {
+ size_t len;
+ limit -= 2; /* make room for quotes */
+ *le++ = '"';
+ len = strlcpy(le, s, limit);
+ if (len >= limit) {
+ le += limit - 3 - 1;
+ *le++ = '.'; *le++ = '.'; *le++ = '.';
+ } else
+ le += len;
+ *le++ = '"';
+ }
+ } break;
+ case POPT_ARG_NONE:
+ default:
+ l = _free(l);
+ return NULL;
+ /*@notreached@*/ break;
+ }
+ *le++ = ')';
+ *le = '\0';
+/*@=boundswrite@*/
+
+ return l;
+}
+
+/**
+ * Display help text for an option.
+ * @param fp output file handle
+ * @param maxLeftCol largest argument display width
+ * @param opt option(s)
+ * @param translation_domain translation domain
+ */
+static void singleOptionHelp(FILE * fp, size_t maxLeftCol,
+ const struct poptOption * opt,
+ /*@null@*/ UNUSED(const char * translation_domain))
+ /*@globals fileSystem @*/
+ /*@modifies *fp, fileSystem @*/
+{
+ size_t indentLength = maxLeftCol + 5;
+ size_t lineLength = 79 - indentLength;
+ const char * help = D_(translation_domain, opt->descrip);
+ const char * argDescrip = getArgDescrip(opt, translation_domain);
+ size_t helpLength;
+ char * defs = NULL;
+ char * left;
+ size_t lelen, limit;
+ size_t nb = maxLeftCol + 1;
+ int displaypad = 0;
+
+ /* Make sure there's more than enough room in target buffer. */
+ if (opt->longName) nb += strlen(opt->longName);
+ if (argDescrip) nb += strlen(argDescrip);
+
+/*@-boundswrite@*/
+ left = malloc(nb);
+ if (left == NULL) return; /* XXX can't happen */
+ left[0] = '\0';
+ left[maxLeftCol] = '\0';
+
+ if (opt->longName && opt->shortName)
+ snprintf(left, nb, "-%c, %s%s", opt->shortName,
+ ((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "-" : "--"),
+ opt->longName);
+ else if (opt->shortName != '\0')
+ snprintf(left, nb, "-%c", opt->shortName);
+ else if (opt->longName)
+ snprintf(left, nb, "%s%s",
+ ((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "-" : "--"),
+ opt->longName);
+ if (!*left) goto out;
+
+ if (argDescrip) {
+ char * le = left + strlen(left);
+
+ if (opt->argInfo & POPT_ARGFLAG_OPTIONAL)
+ *le++ = '[';
+
+ /* Choose type of output */
+ /*@-branchstate@*/
+ if (opt->argInfo & POPT_ARGFLAG_SHOW_DEFAULT) {
+ defs = singleOptionDefaultValue(lineLength, opt, translation_domain);
+ if (defs) {
+ size_t bufsize = (help ? strlen(help) : 0) + sizeof " " + strlen(defs);
+ char * t = malloc(bufsize);
+ if (t) {
+ snprintf(t, bufsize, "%s %s", help ? help : "", defs);
+ defs = _free(defs);
+ }
+ defs = t;
+ }
+ }
+ /*@=branchstate@*/
+
+ if (opt->argDescrip == NULL) {
+ switch (opt->argInfo & POPT_ARG_MASK) {
+ case POPT_ARG_NONE:
+ break;
+ case POPT_ARG_VAL:
+#ifdef NOTNOW /* XXX pug ugly nerdy output */
+ { long aLong = opt->val;
+ int ops = (opt->argInfo & POPT_ARGFLAG_LOGICALOPS);
+ int negate = (opt->argInfo & POPT_ARGFLAG_NOT);
+
+ /* Don't bother displaying typical values */
+ if (!ops && (aLong == 0L || aLong == 1L || aLong == -1L))
+ break;
+ *le++ = '[';
+ switch (ops) {
+ case POPT_ARGFLAG_OR:
+ *le++ = '|';
+ /*@innerbreak@*/ break;
+ case POPT_ARGFLAG_AND:
+ *le++ = '&';
+ /*@innerbreak@*/ break;
+ case POPT_ARGFLAG_XOR:
+ *le++ = '^';
+ /*@innerbreak@*/ break;
+ default:
+ /*@innerbreak@*/ break;
+ }
+ *le++ = (opt->longName != NULL ? '=' : ' ');
+ if (negate) *le++ = '~';
+ /*@-formatconst@*/
+ limit = nb - (le - left);
+ lelen = snprintf(le, limit, (ops ? "0x%lx" : "%ld"), aLong);
+ le += lelen >= limit ? limit - 1 : lelen;
+ /*@=formatconst@*/
+ *le++ = ']';
+ }
+#endif
+ break;
+ case POPT_ARG_INT:
+ case POPT_ARG_LONG:
+ case POPT_ARG_FLOAT:
+ case POPT_ARG_DOUBLE:
+ case POPT_ARG_STRING:
+ *le++ = (opt->longName != NULL ? '=' : ' ');
+ limit = nb - (le - left);
+ lelen = strlcpy(le, argDescrip, limit);
+ le += lelen >= limit ? limit - 1 : lelen;
+ break;
+ default:
+ break;
+ }
+ } else {
+
+ *le++ = '=';
+ limit = nb - (le - left);
+ lelen = strlcpy(le, argDescrip, limit);
+ if (lelen >= limit)
+ lelen = limit - 1;
+ le += lelen;
+
+#ifdef POPT_WCHAR_HACK
+ { const char * scopy = argDescrip;
+ mbstate_t t;
+ size_t n;
+
+ memset ((void *)&t, '\0', sizeof (t)); /* In initial state. */
+ /* Determine number of characters. */
+ n = mbsrtowcs (NULL, &scopy, strlen(scopy), &t);
+
+ displaypad = (int) (lelen-n);
+ }
+#endif
+ }
+ if (opt->argInfo & POPT_ARGFLAG_OPTIONAL)
+ *le++ = ']';
+ *le = '\0';
+ }
+/*@=boundswrite@*/
+
+ if (help)
+ fprintf(fp," %-*s ", (int)maxLeftCol+displaypad, left);
+ else {
+ fprintf(fp," %s\n", left);
+ goto out;
+ }
+
+ left = _free(left);
+/*@-branchstate@*/
+ if (defs) {
+ help = defs;
+ defs = NULL;
+ }
+/*@=branchstate@*/
+
+ helpLength = strlen(help);
+/*@-boundsread@*/
+ while (helpLength > lineLength) {
+ const char * ch;
+ char format[16];
+
+ ch = help + lineLength - 1;
+ while (ch > help && !isSpace(ch)) ch--;
+ if (ch == help) break; /* give up */
+ while (ch > (help + 1) && isSpace(ch)) ch--;
+ ch++;
+
+ snprintf(format, sizeof format, "%%.%ds\n%%%ds", (int) (ch - help), (int) indentLength);
+ /*@-formatconst@*/
+ fprintf(fp, format, help, " ");
+ /*@=formatconst@*/
+ help = ch;
+ while (isSpace(help) && *help) help++;
+ helpLength = strlen(help);
+ }
+/*@=boundsread@*/
+
+ if (helpLength) fprintf(fp, "%s\n", help);
+
+out:
+ /*@-dependenttrans@*/
+ defs = _free(defs);
+ /*@=dependenttrans@*/
+ left = _free(left);
+}
+
+/**
+ * Find display width for longest argument string.
+ * @param opt option(s)
+ * @param translation_domain translation domain
+ * @return display width
+ */
+static size_t maxArgWidth(const struct poptOption * opt,
+ /*@null@*/ UNUSED(const char * translation_domain))
+ /*@*/
+{
+ size_t max = 0;
+ size_t len = 0;
+ const char * s;
+
+ if (opt != NULL)
+ while (opt->longName || opt->shortName || opt->arg) {
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+ if (opt->arg) /* XXX program error */
+ len = maxArgWidth(opt->arg, translation_domain);
+ if (len > max) max = len;
+ } else if (!(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
+ len = sizeof(" ")-1;
+ if (opt->shortName != '\0') len += sizeof("-X")-1;
+ if (opt->shortName != '\0' && opt->longName) len += sizeof(", ")-1;
+ if (opt->longName) {
+ len += ((opt->argInfo & POPT_ARGFLAG_ONEDASH)
+ ? sizeof("-")-1 : sizeof("--")-1);
+ len += strlen(opt->longName);
+ }
+
+ s = getArgDescrip(opt, translation_domain);
+
+#ifdef POPT_WCHAR_HACK
+ /* XXX Calculate no. of display characters. */
+ if (s) {
+ const char * scopy = s;
+ mbstate_t t;
+ size_t n;
+
+/*@-boundswrite@*/
+ memset ((void *)&t, '\0', sizeof (t)); /* In initial state. */
+/*@=boundswrite@*/
+ /* Determine number of characters. */
+ n = mbsrtowcs (NULL, &scopy, strlen(scopy), &t);
+ len += sizeof("=")-1 + n;
+ }
+#else
+ if (s)
+ len += sizeof("=")-1 + strlen(s);
+#endif
+
+ if (opt->argInfo & POPT_ARGFLAG_OPTIONAL) len += sizeof("[]")-1;
+ if (len > max) max = len;
+ }
+
+ opt++;
+ }
+
+ return max;
+}
+
+/**
+ * Display popt alias and exec help.
+ * @param fp output file handle
+ * @param items alias/exec array
+ * @param nitems no. of alias/exec entries
+ * @param left largest argument display width
+ * @param translation_domain translation domain
+ */
+static void itemHelp(FILE * fp,
+ /*@null@*/ poptItem items, int nitems, size_t left,
+ /*@null@*/ UNUSED(const char * translation_domain))
+ /*@globals fileSystem @*/
+ /*@modifies *fp, fileSystem @*/
+{
+ poptItem item;
+ int i;
+
+ if (items != NULL)
+ for (i = 0, item = items; i < nitems; i++, item++) {
+ const struct poptOption * opt;
+ opt = &item->option;
+ if ((opt->longName || opt->shortName) &&
+ !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN))
+ singleOptionHelp(fp, left, opt, translation_domain);
+ }
+}
+
+/**
+ * Display help text for a table of options.
+ * @param con context
+ * @param fp output file handle
+ * @param table option(s)
+ * @param left largest argument display width
+ * @param translation_domain translation domain
+ */
+static void singleTableHelp(poptContext con, FILE * fp,
+ /*@null@*/ const struct poptOption * table, size_t left,
+ /*@null@*/ UNUSED(const char * translation_domain))
+ /*@globals fileSystem @*/
+ /*@modifies *fp, fileSystem @*/
+{
+ const struct poptOption * opt;
+ const char *sub_transdom;
+
+ if (table == poptAliasOptions) {
+ itemHelp(fp, con->aliases, con->numAliases, left, NULL);
+ itemHelp(fp, con->execs, con->numExecs, left, NULL);
+ return;
+ }
+
+ if (table != NULL)
+ for (opt = table; (opt->longName || opt->shortName || opt->arg); opt++) {
+ if ((opt->longName || opt->shortName) &&
+ !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN))
+ singleOptionHelp(fp, left, opt, translation_domain);
+ }
+
+ if (table != NULL)
+ for (opt = table; (opt->longName || opt->shortName || opt->arg); opt++) {
+ if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_INCLUDE_TABLE)
+ continue;
+ sub_transdom = getTableTranslationDomain(opt->arg);
+ if (sub_transdom == NULL)
+ sub_transdom = translation_domain;
+
+ if (opt->descrip)
+ fprintf(fp, "\n%s\n", D_(sub_transdom, opt->descrip));
+
+ singleTableHelp(con, fp, opt->arg, left, sub_transdom);
+ }
+}
+
+/**
+ * @param con context
+ * @param fp output file handle
+ */
+static int showHelpIntro(poptContext con, FILE * fp)
+ /*@globals fileSystem @*/
+ /*@modifies *fp, fileSystem @*/
+{
+ int len = 6;
+ const char * fn;
+
+ fprintf(fp, POPT_("Usage:"));
+ if (!(con->flags & POPT_CONTEXT_KEEP_FIRST)) {
+/*@-boundsread@*/
+ /*@-nullderef -type@*/ /* LCL: wazzup? */
+ fn = con->optionStack->argv[0];
+ /*@=nullderef =type@*/
+/*@=boundsread@*/
+ if (fn == NULL) return len;
+ if (strchr(fn, '/')) fn = strrchr(fn, '/') + 1;
+ fprintf(fp, " %s", fn);
+ len += strlen(fn) + 1;
+ }
+
+ return len;
+}
+
+void poptPrintHelp(poptContext con, FILE * fp, /*@unused@*/ UNUSED(int flags))
+{
+ size_t leftColWidth;
+
+ (void) showHelpIntro(con, fp);
+ if (con->otherHelp)
+ fprintf(fp, " %s\n", con->otherHelp);
+ else
+ fprintf(fp, " %s\n", POPT_("[OPTION...]"));
+
+ leftColWidth = maxArgWidth(con->options, NULL);
+ singleTableHelp(con, fp, con->options, leftColWidth, NULL);
+}
+
+/**
+ * Display usage text for an option.
+ * @param fp output file handle
+ * @param cursor current display position
+ * @param opt option(s)
+ * @param translation_domain translation domain
+ */
+static size_t singleOptionUsage(FILE * fp, size_t cursor,
+ const struct poptOption * opt,
+ /*@null@*/ const char *translation_domain)
+ /*@globals fileSystem @*/
+ /*@modifies *fp, fileSystem @*/
+{
+ size_t len = 4;
+ char shortStr[2] = { '\0', '\0' };
+ const char * item = shortStr;
+ const char * argDescrip = getArgDescrip(opt, translation_domain);
+
+ if (opt->shortName != '\0' && opt->longName != NULL) {
+ len += 2;
+ if (!(opt->argInfo & POPT_ARGFLAG_ONEDASH)) len++;
+ len += strlen(opt->longName);
+ } else if (opt->shortName != '\0') {
+ len++;
+ shortStr[0] = opt->shortName;
+ shortStr[1] = '\0';
+ } else if (opt->longName) {
+ len += strlen(opt->longName);
+ if (!(opt->argInfo & POPT_ARGFLAG_ONEDASH)) len++;
+ item = opt->longName;
+ }
+
+ if (len == 4) return cursor;
+
+#ifdef POPT_WCHAR_HACK
+ /* XXX Calculate no. of display characters. */
+ if (argDescrip) {
+ const char * scopy = argDescrip;
+ mbstate_t t;
+ size_t n;
+
+/*@-boundswrite@*/
+ memset ((void *)&t, '\0', sizeof (t)); /* In initial state. */
+/*@=boundswrite@*/
+ /* Determine number of characters. */
+ n = mbsrtowcs (NULL, &scopy, strlen(scopy), &t);
+ len += sizeof("=")-1 + n;
+ }
+#else
+ if (argDescrip)
+ len += sizeof("=")-1 + strlen(argDescrip);
+#endif
+
+ if ((cursor + len) > 79) {
+ fprintf(fp, "\n ");
+ cursor = 7;
+ }
+
+ if (opt->longName && opt->shortName) {
+ fprintf(fp, " [-%c|-%s%s%s%s]",
+ opt->shortName, ((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "" : "-"),
+ opt->longName,
+ (argDescrip ? " " : ""),
+ (argDescrip ? argDescrip : ""));
+ } else {
+ fprintf(fp, " [-%s%s%s%s]",
+ ((opt->shortName || (opt->argInfo & POPT_ARGFLAG_ONEDASH)) ? "" : "-"),
+ item,
+ (argDescrip ? (opt->shortName != '\0' ? " " : "=") : ""),
+ (argDescrip ? argDescrip : ""));
+ }
+
+ return cursor + len + 1;
+}
+
+/**
+ * Display popt alias and exec usage.
+ * @param fp output file handle
+ * @param cursor current display position
+ * @param item alias/exec array
+ * @param nitems no. of ara/exec entries
+ * @param translation_domain translation domain
+ */
+static size_t itemUsage(FILE * fp, size_t cursor,
+ /*@null@*/ poptItem item, int nitems,
+ /*@null@*/ UNUSED(const char * translation_domain))
+ /*@globals fileSystem @*/
+ /*@modifies *fp, fileSystem @*/
+{
+ int i;
+
+ /*@-branchstate@*/ /* FIX: W2DO? */
+ if (item != NULL)
+ for (i = 0; i < nitems; i++, item++) {
+ const struct poptOption * opt;
+ opt = &item->option;
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INTL_DOMAIN) {
+ translation_domain = (const char *)opt->arg;
+ } else if ((opt->longName || opt->shortName) &&
+ !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
+ cursor = singleOptionUsage(fp, cursor, opt, translation_domain);
+ }
+ }
+ /*@=branchstate@*/
+
+ return cursor;
+}
+
+/**
+ * Keep track of option tables already processed.
+ */
+typedef struct poptDone_s {
+ int nopts;
+ int maxopts;
+ const void ** opts;
+} * poptDone;
+
+/**
+ * Display usage text for a table of options.
+ * @param con context
+ * @param fp output file handle
+ * @param cursor current display position
+ * @param opt option(s)
+ * @param translation_domain translation domain
+ * @param done tables already processed
+ * @return
+ */
+static size_t singleTableUsage(poptContext con, FILE * fp, size_t cursor,
+ /*@null@*/ const struct poptOption * opt,
+ /*@null@*/ UNUSED(const char * translation_domain),
+ /*@null@*/ poptDone done)
+ /*@globals fileSystem @*/
+ /*@modifies *fp, done, fileSystem @*/
+{
+ /*@-branchstate@*/ /* FIX: W2DO? */
+ if (opt != NULL)
+ for (; (opt->longName || opt->shortName || opt->arg) ; opt++) {
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INTL_DOMAIN) {
+ translation_domain = (const char *)opt->arg;
+ } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+ if (done) {
+ int i = 0;
+ for (i = 0; i < done->nopts; i++) {
+/*@-boundsread@*/
+ const void * that = done->opts[i];
+/*@=boundsread@*/
+ if (that == NULL || that != opt->arg)
+ /*@innercontinue@*/ continue;
+ /*@innerbreak@*/ break;
+ }
+ /* Skip if this table has already been processed. */
+ if (opt->arg == NULL || i < done->nopts)
+ continue;
+/*@-boundswrite@*/
+ if (done->nopts < done->maxopts)
+ done->opts[done->nopts++] = (const void *) opt->arg;
+/*@=boundswrite@*/
+ }
+ cursor = singleTableUsage(con, fp, cursor, opt->arg,
+ translation_domain, done);
+ } else if ((opt->longName || opt->shortName) &&
+ !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
+ cursor = singleOptionUsage(fp, cursor, opt, translation_domain);
+ }
+ }
+ /*@=branchstate@*/
+
+ return cursor;
+}
+
+/**
+ * Return concatenated short options for display.
+ * @todo Sub-tables should be recursed.
+ * @param opt option(s)
+ * @param fp output file handle
+ * @retval str concatenation of short options
+ * @return length of display string
+ */
+static int showShortOptions(const struct poptOption * opt, FILE * fp,
+ /*@null@*/ char * str)
+ /*@globals fileSystem @*/
+ /*@modifies *str, *fp, fileSystem @*/
+ /*@requires maxRead(str) >= 0 @*/
+{
+ /* bufsize larger then the ascii set, lazy alloca on top level call. */
+ char * s = (str != NULL ? str : memset(alloca(300), 0, 300));
+ int len = 0;
+
+ if (s == NULL)
+ return 0;
+
+/*@-boundswrite@*/
+ if (opt != NULL)
+ for (; (opt->longName || opt->shortName || opt->arg); opt++) {
+ if (opt->shortName && !(opt->argInfo & POPT_ARG_MASK))
+ s[strlen(s)] = opt->shortName;
+ else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE)
+ if (opt->arg) /* XXX program error */
+ len = showShortOptions(opt->arg, fp, s);
+ }
+/*@=boundswrite@*/
+
+ /* On return to top level, print the short options, return print length. */
+ if (s == str && *s != '\0') {
+ fprintf(fp, " [-%s]", s);
+ len = strlen(s) + sizeof(" [-]")-1;
+ }
+ return len;
+}
+
+void poptPrintUsage(poptContext con, FILE * fp, /*@unused@*/ UNUSED(int flags))
+{
+ poptDone done = memset(alloca(sizeof(*done)), 0, sizeof(*done));
+ size_t cursor;
+
+ done->nopts = 0;
+ done->maxopts = 64;
+ cursor = done->maxopts * sizeof(*done->opts);
+/*@-boundswrite@*/
+ done->opts = memset(alloca(cursor), 0, cursor);
+ /*@-keeptrans@*/
+ done->opts[done->nopts++] = (const void *) con->options;
+ /*@=keeptrans@*/
+/*@=boundswrite@*/
+
+ cursor = showHelpIntro(con, fp);
+ cursor += showShortOptions(con->options, fp, NULL);
+ cursor = singleTableUsage(con, fp, cursor, con->options, NULL, done);
+ cursor = itemUsage(fp, cursor, con->aliases, con->numAliases, NULL);
+ cursor = itemUsage(fp, cursor, con->execs, con->numExecs, NULL);
+
+ if (con->otherHelp) {
+ cursor += strlen(con->otherHelp) + 1;
+ if (cursor > 79) fprintf(fp, "\n ");
+ fprintf(fp, " %s", con->otherHelp);
+ }
+
+ fprintf(fp, "\n");
+}
+
+void poptSetOtherOptionHelp(poptContext con, const char * text)
+{
+ con->otherHelp = _free(con->otherHelp);
+ con->otherHelp = xstrdup(text);
+}
diff --git a/rsync/popt/poptint.h b/rsync/popt/poptint.h
new file mode 100644
index 0000000..bec7c97
--- /dev/null
+++ b/rsync/popt/poptint.h
@@ -0,0 +1,122 @@
+/** \ingroup popt
+ * \file popt/poptint.h
+ */
+
+/* (C) 1998-2000 Red Hat, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.rpm.org/pub/rpm/dist. */
+
+#ifndef H_POPTINT
+#define H_POPTINT
+
+/**
+ * Wrapper to free(3), hides const compilation noise, permit NULL, return NULL.
+ * @param p memory to free
+ * @retval NULL always
+ */
+/*@unused@*/ static inline /*@null@*/ void *
+_free(/*@only@*/ /*@null@*/ const void * p)
+ /*@modifies p @*/
+{
+ if (p != NULL) free((void *)p);
+ return NULL;
+}
+
+static inline int
+isSpace(const char *ptr)
+{
+ return isspace(*(unsigned char *)ptr);
+}
+
+/* Bit mask macros. */
+/*@-exporttype -redef @*/
+typedef unsigned int __pbm_bits;
+/*@=exporttype =redef @*/
+#define __PBM_NBITS (8 * sizeof (__pbm_bits))
+#define __PBM_IX(d) ((d) / __PBM_NBITS)
+#define __PBM_MASK(d) ((__pbm_bits) 1 << (((unsigned)(d)) % __PBM_NBITS))
+/*@-exporttype -redef @*/
+typedef struct {
+ __pbm_bits bits[1];
+} pbm_set;
+/*@=exporttype =redef @*/
+#define __PBM_BITS(set) ((set)->bits)
+
+#define PBM_ALLOC(d) calloc(__PBM_IX (d) + 1, sizeof(__pbm_bits))
+#define PBM_FREE(s) _free(s);
+#define PBM_SET(d, s) (__PBM_BITS (s)[__PBM_IX (d)] |= __PBM_MASK (d))
+#define PBM_CLR(d, s) (__PBM_BITS (s)[__PBM_IX (d)] &= ~__PBM_MASK (d))
+#define PBM_ISSET(d, s) ((__PBM_BITS (s)[__PBM_IX (d)] & __PBM_MASK (d)) != 0)
+
+struct optionStackEntry {
+ int argc;
+/*@only@*/ /*@null@*/
+ const char ** argv;
+/*@only@*/ /*@null@*/
+ pbm_set * argb;
+ int next;
+/*@only@*/ /*@null@*/
+ const char * nextArg;
+/*@observer@*/ /*@null@*/
+ const char * nextCharArg;
+/*@dependent@*/ /*@null@*/
+ poptItem currAlias;
+ int stuffed;
+};
+
+struct poptContext_s {
+ struct optionStackEntry optionStack[POPT_OPTION_DEPTH];
+/*@dependent@*/
+ struct optionStackEntry * os;
+/*@owned@*/ /*@null@*/
+ const char ** leftovers;
+ int numLeftovers;
+ int nextLeftover;
+/*@keep@*/
+ const struct poptOption * options;
+ int restLeftover;
+/*@only@*/ /*@null@*/
+ const char * appName;
+/*@only@*/ /*@null@*/
+ poptItem aliases;
+ int numAliases;
+ int flags;
+/*@owned@*/ /*@null@*/
+ poptItem execs;
+ int numExecs;
+/*@only@*/ /*@null@*/
+ const char ** finalArgv;
+ int finalArgvCount;
+ int finalArgvAlloced;
+/*@dependent@*/ /*@null@*/
+ poptItem doExec;
+/*@only@*/
+ const char * execPath;
+ int execAbsolute;
+/*@only@*/ /*@relnull@*/
+ const char * otherHelp;
+/*@null@*/
+ pbm_set * arg_strip;
+};
+
+#ifdef HAVE_LIBINTL_H
+#include
+#endif
+
+#if defined(HAVE_GETTEXT) && !defined(__LCLINT__)
+#define _(foo) gettext(foo)
+#else
+#define _(foo) foo
+#endif
+
+#if defined(HAVE_DCGETTEXT) && !defined(__LCLINT__)
+#define D_(dom, str) dgettext(dom, str)
+#define POPT_(foo) D_("popt", foo)
+#else
+#define D_(dom, str) str
+#define POPT_(foo) foo
+#endif
+
+#define N_(foo) foo
+
+#endif
diff --git a/rsync/popt/poptparse.c b/rsync/popt/poptparse.c
new file mode 100644
index 0000000..e003a04
--- /dev/null
+++ b/rsync/popt/poptparse.c
@@ -0,0 +1,231 @@
+/** \ingroup popt
+ * \file popt/poptparse.c
+ */
+
+/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.rpm.org/pub/rpm/dist. */
+
+#include "system.h"
+
+#include "poptint.h"
+
+#define POPT_ARGV_ARRAY_GROW_DELTA 5
+
+/*@-boundswrite@*/
+int poptDupArgv(int argc, const char **argv,
+ int * argcPtr, const char *** argvPtr)
+{
+ size_t nb = (argc + 1) * sizeof(*argv);
+ const char ** argv2;
+ char * dst;
+ int i;
+
+ if (argc <= 0 || argv == NULL) /* XXX can't happen */
+ return POPT_ERROR_NOARG;
+ for (i = 0; i < argc; i++) {
+ if (argv[i] == NULL)
+ return POPT_ERROR_NOARG;
+ nb += strlen(argv[i]) + 1;
+ }
+
+ dst = malloc(nb);
+ if (dst == NULL) /* XXX can't happen */
+ return POPT_ERROR_MALLOC;
+ argv2 = (void *) dst;
+ dst += (argc + 1) * sizeof(*argv);
+
+ /*@-branchstate@*/
+ for (i = 0; i < argc; i++) {
+ argv2[i] = dst;
+ dst += strlcpy(dst, argv[i], nb) + 1;
+ }
+ /*@=branchstate@*/
+ argv2[argc] = NULL;
+
+ if (argvPtr) {
+ *argvPtr = argv2;
+ } else {
+ free(argv2);
+ argv2 = NULL;
+ }
+ if (argcPtr)
+ *argcPtr = argc;
+ return 0;
+}
+/*@=boundswrite@*/
+
+/*@-bounds@*/
+int poptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
+{
+ const char * src;
+ char quote = '\0';
+ int argvAlloced = POPT_ARGV_ARRAY_GROW_DELTA;
+ const char ** argv = malloc(sizeof(*argv) * argvAlloced);
+ int argc = 0;
+ int buflen = strlen(s) + 1;
+ char * buf = memset(alloca(buflen), 0, buflen);
+ int rc = POPT_ERROR_MALLOC;
+
+ if (argv == NULL) return rc;
+ argv[argc] = buf;
+
+ for (src = s; *src != '\0'; src++) {
+ if (quote == *src) {
+ quote = '\0';
+ } else if (quote != '\0') {
+ if (*src == '\\') {
+ src++;
+ if (!*src) {
+ rc = POPT_ERROR_BADQUOTE;
+ goto exit;
+ }
+ if (*src != quote) *buf++ = '\\';
+ }
+ *buf++ = *src;
+ } else if (isSpace(src)) {
+ if (*argv[argc] != '\0') {
+ buf++, argc++;
+ if (argc == argvAlloced) {
+ argvAlloced += POPT_ARGV_ARRAY_GROW_DELTA;
+ argv = realloc(argv, sizeof(*argv) * argvAlloced);
+ if (argv == NULL) goto exit;
+ }
+ argv[argc] = buf;
+ }
+ } else switch (*src) {
+ case '"':
+ case '\'':
+ quote = *src;
+ /*@switchbreak@*/ break;
+ case '\\':
+ src++;
+ if (!*src) {
+ rc = POPT_ERROR_BADQUOTE;
+ goto exit;
+ }
+ /*@fallthrough@*/
+ default:
+ *buf++ = *src;
+ /*@switchbreak@*/ break;
+ }
+ }
+
+ if (strlen(argv[argc])) {
+ argc++, buf++;
+ }
+
+ rc = poptDupArgv(argc, argv, argcPtr, argvPtr);
+
+exit:
+ if (argv) free(argv);
+ return rc;
+}
+/*@=bounds@*/
+
+/* still in the dev stage.
+ * return values, perhaps 1== file erro
+ * 2== line to long
+ * 3== umm.... more?
+ */
+int poptConfigFileToString(FILE *fp, char ** argstrp, /*@unused@*/ UNUSED(int flags))
+{
+ char line[999];
+ char * argstr;
+ char * p;
+ char * q;
+ char * x;
+ int t;
+ int argvlen = 0;
+ size_t maxlinelen = sizeof(line);
+ size_t linelen;
+ int maxargvlen = 480;
+ int linenum = 0;
+
+ *argstrp = NULL;
+
+ /* | this_is = our_line
+ * p q x
+ */
+
+ if (fp == NULL)
+ return POPT_ERROR_NULLARG;
+
+ argstr = calloc(maxargvlen, sizeof(*argstr));
+ if (argstr == NULL) return POPT_ERROR_MALLOC;
+
+ while (fgets(line, (int)maxlinelen, fp) != NULL) {
+ linenum++;
+ p = line;
+
+ /* loop until first non-space char or EOL */
+ while( *p != '\0' && isSpace(p) )
+ p++;
+
+ linelen = strlen(p);
+ if (linelen >= maxlinelen-1) {
+ free(argstr);
+ return POPT_ERROR_OVERFLOW; /* XXX line too long */
+ }
+
+ if (*p == '\0' || *p == '\n') continue; /* line is empty */
+ if (*p == '#') continue; /* comment line */
+
+ q = p;
+
+ while (*q != '\0' && (!isSpace(q)) && *q != '=')
+ q++;
+
+ if (isSpace(q)) {
+ /* a space after the name, find next non space */
+ *q++='\0';
+ while( *q != '\0' && isSpace(q) ) q++;
+ }
+ if (*q == '\0') {
+ /* single command line option (ie, no name=val, just name) */
+ q[-1] = '\0'; /* kill off newline from fgets() call */
+ argvlen += (t = q - p) + (sizeof(" --")-1);
+ if (argvlen >= maxargvlen) {
+ maxargvlen = (t > maxargvlen) ? t*2 : maxargvlen*2;
+ argstr = realloc(argstr, maxargvlen);
+ if (argstr == NULL) return POPT_ERROR_MALLOC;
+ }
+ strlcat(argstr, " --", maxargvlen);
+ strlcat(argstr, p, maxargvlen);
+ continue;
+ }
+ if (*q != '=')
+ continue; /* XXX for now, silently ignore bogus line */
+
+ /* *q is an equal sign. */
+ *q++ = '\0';
+
+ /* find next non-space letter of value */
+ while (*q != '\0' && isSpace(q))
+ q++;
+ if (*q == '\0')
+ continue; /* XXX silently ignore missing value */
+
+ /* now, loop and strip all ending whitespace */
+ x = p + linelen;
+ while (isSpace(--x))
+ *x = 0; /* null out last char if space (including fgets() NL) */
+
+ /* rest of line accept */
+ t = x - p;
+ argvlen += t + (sizeof("' --='")-1);
+ if (argvlen >= maxargvlen) {
+ maxargvlen = (t > maxargvlen) ? t*2 : maxargvlen*2;
+ argstr = realloc(argstr, maxargvlen);
+ if (argstr == NULL) return POPT_ERROR_MALLOC;
+ }
+ strlcat(argstr, " --", maxargvlen);
+ strlcat(argstr, p, maxargvlen);
+ strlcat(argstr, "=\"", maxargvlen);
+ strlcat(argstr, q, maxargvlen);
+ strlcat(argstr, "\"", maxargvlen);
+ }
+
+ *argstrp = argstr;
+ return 0;
+}
diff --git a/rsync/popt/system.h b/rsync/popt/system.h
new file mode 100644
index 0000000..50cecaf
--- /dev/null
+++ b/rsync/popt/system.h
@@ -0,0 +1,130 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if defined (__GLIBC__) && defined(__LCLINT__)
+/*@-declundef@*/
+/*@unchecked@*/
+extern __const __int32_t *__ctype_tolower;
+/*@unchecked@*/
+extern __const __int32_t *__ctype_toupper;
+/*@=declundef@*/
+#endif
+
+#include
+
+#include
+#include
+#include
+
+#if HAVE_MCHECK_H
+#include
+#endif
+
+#include
+#ifdef HAVE_SYS_TYPES_H
+# include
+#endif
+#ifdef STDC_HEADERS
+# include
+# include
+#else
+# ifdef HAVE_STDLIB_H
+# include
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+# include
+# endif
+# include
+#endif
+#ifdef HAVE_STRINGS_H
+# include
+#endif
+#ifdef HAVE_UNISTD_H
+# include
+#endif
+
+#ifndef __GNUC__
+#define __attribute__(x)
+#endif
+
+#ifdef __NeXT
+/* access macros are not declared in non posix mode in unistd.h -
+ don't try to use posix on NeXTstep 3.3 ! */
+#include
+#endif
+
+#if defined(__LCLINT__)
+/*@-declundef -incondefs @*/ /* LCL: missing annotation */
+/*@only@*/ /*@out@*/
+void * alloca (size_t __size)
+ /*@ensures MaxSet(result) == (__size - 1) @*/
+ /*@*/;
+/*@=declundef =incondefs @*/
+#endif
+
+/* AIX requires this to be the first thing in the file. */
+#ifndef __GNUC__
+# if HAVE_ALLOCA_H
+# include
+# else
+# ifdef _AIX
+#pragma alloca
+# else
+# ifdef HAVE_ALLOCA
+# ifndef alloca /* predefined by HP cc +Olibcalls */
+char *alloca(size_t size);
+# endif
+# else
+# ifdef alloca
+# undef alloca
+# endif
+# define alloca(sz) malloc(sz) /* Kludge this for now */
+# endif
+# endif
+# endif
+#elif !defined(alloca)
+#define alloca __builtin_alloca
+#endif
+
+#ifndef HAVE_STRLCPY
+size_t strlcpy(char *d, const char *s, size_t bufsize);
+#endif
+
+#ifndef HAVE_STRLCAT
+size_t strlcat(char *d, const char *s, size_t bufsize);
+#endif
+
+#if HAVE_MCHECK_H && defined(__GNUC__)
+static inline char *
+xstrdup(const char *s)
+{
+ size_t memsize = strlen(s) + 1;
+ char *ptr = malloc(memsize);
+ if (!ptr) {
+ fprintf(stderr, "virtual memory exhausted.\n");
+ exit(EXIT_FAILURE);
+ }
+ strlcpy(ptr, s, memsize);
+ return ptr;
+}
+#else
+#define xstrdup(_str) strdup(_str)
+#endif /* HAVE_MCHECK_H && defined(__GNUC__) */
+
+#if HAVE___SECURE_GETENV && !defined(__LCLINT__)
+#define getenv(_s) __secure_getenv(_s)
+#endif
+
+#if !defined HAVE_SNPRINTF || !defined HAVE_C99_VSNPRINTF
+#define snprintf rsync_snprintf
+int snprintf(char *str,size_t count,const char *fmt,...);
+#endif
+
+#define UNUSED(x) x __attribute__((__unused__))
+
+#define PACKAGE "rsync"
+
+#include "popt.h"
diff --git a/rsync/prepare-source b/rsync/prepare-source
new file mode 100755
index 0000000..0e73138
--- /dev/null
+++ b/rsync/prepare-source
@@ -0,0 +1,51 @@
+#!/bin/sh
+# Either use autoconf and autoheader to create configure.sh and config.h.in
+# or (optionally) fetch the latest development versions of generated files.
+#
+# Specify one action or more than one to provide a fall-back:
+#
+# build build the config files [the default w/no arg]
+# fetch fetch the latest dev config files
+# fetchgen fetch all the latest dev generated files
+# fetchSRC fetch the latest dev source files [NON-GENERATED FILES]
+#
+# The script stops after the first successful action.
+
+dir=`dirname $0`
+if test x"$dir" != x -a x"$dir" != x.; then
+ cd "$dir"
+fi
+
+if test $# = 0; then
+ set -- build
+fi
+
+for action in "${@}"; do
+ case "$action" in
+ build|make)
+ make -f prepare-source.mak
+ ;;
+ fetch)
+ if perl --version >/dev/null 2>/dev/null; then
+ files='c*'
+ else
+ files='[cp]*'
+ fi
+ rsync -pvz rsync://rsync.samba.org/rsyncftp/generated-files/"$files" .
+ ;;
+ fetchgen)
+ rsync -pvz rsync://rsync.samba.org/rsyncftp/generated-files/'*' .
+ ;;
+ fetchSRC)
+ rsync -pvrz --exclude=/.git/ rsync://rsync.samba.org/ftp/pub/unpacked/rsync/ .
+ ;;
+ *)
+ echo "Unknown action: $action"
+ exit 1
+ esac
+ if test $? = 0; then
+ exit
+ fi
+done
+
+exit 1
diff --git a/rsync/prepare-source.mak b/rsync/prepare-source.mak
new file mode 100644
index 0000000..054bab7
--- /dev/null
+++ b/rsync/prepare-source.mak
@@ -0,0 +1,7 @@
+conf: configure.sh config.h.in
+
+configure.sh: configure.ac aclocal.m4
+ autoconf -o configure.sh
+
+config.h.in: configure.ac aclocal.m4
+ autoheader && touch config.h.in
diff --git a/rsync/progress.c b/rsync/progress.c
new file mode 100644
index 0000000..318a77f
--- /dev/null
+++ b/rsync/progress.c
@@ -0,0 +1,221 @@
+/*
+ * Routines to output progress information during a file transfer.
+ *
+ * Copyright (C) 1996-2000 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2001, 2002 Martin Pool
+ * Copyright (C) 2003-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "inums.h"
+
+extern int am_server;
+extern int flist_eof;
+extern int need_unsorted_flist;
+extern int output_needs_newline;
+extern struct stats stats;
+extern struct file_list *cur_flist;
+
+#define PROGRESS_HISTORY_SECS 5
+
+#ifdef GETPGRP_VOID
+#define GETPGRP_ARG
+#else
+#define GETPGRP_ARG 0
+#endif
+
+struct progress_history {
+ struct timeval time;
+ OFF_T ofs;
+};
+
+static struct progress_history ph_start;
+static struct progress_history ph_list[PROGRESS_HISTORY_SECS];
+static int newest_hpos, oldest_hpos;
+static int current_file_index;
+
+static unsigned long msdiff(struct timeval *t1, struct timeval *t2)
+{
+ return (t2->tv_sec - t1->tv_sec) * 1000L
+ + (t2->tv_usec - t1->tv_usec) / 1000;
+}
+
+
+/**
+ * @param ofs Current position in file
+ * @param size Total size of file
+ * @param is_last True if this is the last time progress will be
+ * printed for this file, so we should output a newline. (Not
+ * necessarily the same as all bytes being received.)
+ **/
+static void rprint_progress(OFF_T ofs, OFF_T size, struct timeval *now,
+ int is_last)
+{
+ char rembuf[64], eol[128];
+ const char *units;
+ unsigned long diff;
+ double rate, remain;
+ int pct;
+
+ if (is_last) {
+ int len = snprintf(eol, sizeof eol,
+ " (xfr#%d, %s-chk=%d/%d)\n",
+ stats.xferred_files, flist_eof ? "to" : "ir",
+ stats.num_files - current_file_index - 1,
+ stats.num_files);
+ if (INFO_GTE(PROGRESS, 2)) {
+ static int last_len = 0;
+ /* Drop \n and pad with spaces if line got shorter. */
+ if (last_len < --len)
+ last_len = len;
+ eol[last_len] = '\0';
+ while (last_len > len)
+ eol[--last_len] = ' ';
+ is_last = 0;
+ }
+ /* Compute stats based on the starting info. */
+ if (!ph_start.time.tv_sec
+ || !(diff = msdiff(&ph_start.time, now)))
+ diff = 1;
+ rate = (double) (ofs - ph_start.ofs) * 1000.0 / diff / 1024.0;
+ /* Switch to total time taken for our last update. */
+ remain = (double) diff / 1000.0;
+ } else {
+ strlcpy(eol, " ", sizeof eol);
+ /* Compute stats based on recent progress. */
+ if (!(diff = msdiff(&ph_list[oldest_hpos].time, now)))
+ diff = 1;
+ rate = (double) (ofs - ph_list[oldest_hpos].ofs) * 1000.0
+ / diff / 1024.0;
+ remain = rate ? (double) (size - ofs) / rate / 1000.0 : 0.0;
+ }
+
+ if (rate > 1024*1024) {
+ rate /= 1024.0 * 1024.0;
+ units = "GB/s";
+ } else if (rate > 1024) {
+ rate /= 1024.0;
+ units = "MB/s";
+ } else {
+ units = "kB/s";
+ }
+
+ if (remain < 0)
+ strlcpy(rembuf, " ??:??:??", sizeof rembuf);
+ else {
+ snprintf(rembuf, sizeof rembuf, "%4d:%02d:%02d",
+ (int) (remain / 3600.0),
+ (int) (remain / 60.0) % 60,
+ (int) remain % 60);
+ }
+
+ output_needs_newline = 0;
+ pct = ofs == size ? 100 : (int) (100.0 * ofs / size);
+ rprintf(FCLIENT, "\r%15s %3d%% %7.2f%s %s%s",
+ human_num(ofs), pct, rate, units, rembuf, eol);
+ if (!is_last) {
+ output_needs_newline = 1;
+ rflush(FCLIENT);
+ }
+}
+
+void set_current_file_index(struct file_struct *file, int ndx)
+{
+ if (!file)
+ current_file_index = cur_flist->used + cur_flist->ndx_start - 1;
+ else if (need_unsorted_flist)
+ current_file_index = flist_find(cur_flist, file) + cur_flist->ndx_start;
+ else
+ current_file_index = ndx;
+ current_file_index -= cur_flist->flist_num;
+}
+
+void end_progress(OFF_T size)
+{
+ if (!am_server) {
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ if (INFO_GTE(PROGRESS, 2)) {
+ rprint_progress(stats.total_transferred_size,
+ stats.total_size, &now, True);
+ } else {
+ rprint_progress(size, size, &now, True);
+ memset(&ph_start, 0, sizeof ph_start);
+ }
+ }
+}
+
+void show_progress(OFF_T ofs, OFF_T size)
+{
+ struct timeval now;
+#if defined HAVE_GETPGRP && defined HAVE_TCGETPGRP
+ static pid_t pgrp = -1;
+ pid_t tc_pgrp;
+#endif
+
+ if (am_server)
+ return;
+
+#if defined HAVE_GETPGRP && defined HAVE_TCGETPGRP
+ if (pgrp == -1)
+ pgrp = getpgrp(GETPGRP_ARG);
+#endif
+
+ gettimeofday(&now, NULL);
+
+ if (INFO_GTE(PROGRESS, 2)) {
+ ofs = stats.total_transferred_size - size + ofs;
+ size = stats.total_size;
+ }
+
+ if (!ph_start.time.tv_sec) {
+ int i;
+
+ /* Try to guess the real starting time when the sender started
+ * to send us data by using the time we last received some data
+ * in the last file (if it was recent enough). */
+ if (msdiff(&ph_list[newest_hpos].time, &now) <= 1500) {
+ ph_start.time = ph_list[newest_hpos].time;
+ ph_start.ofs = 0;
+ } else {
+ ph_start.time.tv_sec = now.tv_sec;
+ ph_start.time.tv_usec = now.tv_usec;
+ ph_start.ofs = ofs;
+ }
+
+ for (i = 0; i < PROGRESS_HISTORY_SECS; i++)
+ ph_list[i] = ph_start;
+ }
+ else {
+ if (msdiff(&ph_list[newest_hpos].time, &now) < 1000)
+ return;
+
+ newest_hpos = oldest_hpos;
+ oldest_hpos = (oldest_hpos + 1) % PROGRESS_HISTORY_SECS;
+ ph_list[newest_hpos].time.tv_sec = now.tv_sec;
+ ph_list[newest_hpos].time.tv_usec = now.tv_usec;
+ ph_list[newest_hpos].ofs = ofs;
+ }
+
+#if defined HAVE_GETPGRP && defined HAVE_TCGETPGRP
+ tc_pgrp = tcgetpgrp(STDOUT_FILENO);
+ if (tc_pgrp != pgrp && tc_pgrp != -1)
+ return;
+#endif
+
+ rprint_progress(ofs, size, &now, False);
+}
diff --git a/rsync/proto.h b/rsync/proto.h
new file mode 100644
index 0000000..22fc539
--- /dev/null
+++ b/rsync/proto.h
@@ -0,0 +1,423 @@
+/* This file is automatically generated with "make proto". DO NOT EDIT */
+
+int allow_access(const char *addr, const char **host_ptr, int i);
+void free_acl(stat_x *sxp);
+int get_acl(const char *fname, stat_x *sxp);
+void send_acl(int f, stat_x *sxp);
+void receive_acl(int f, struct file_struct *file);
+void cache_tmp_acl(struct file_struct *file, stat_x *sxp);
+void uncache_tmp_acls(void);
+int set_acl(const char *fname, const struct file_struct *file, stat_x *sxp, mode_t new_mode);
+void match_acl_ids(void);
+int default_perms_for_dir(const char *dir);
+void base64_encode(const char *buf, int len, char *out, int pad);
+char *auth_server(int f_in, int f_out, int module, const char *host,
+ const char *addr, const char *leader);
+void auth_client(int fd, const char *user, const char *challenge);
+char *get_backup_name(const char *fname);
+int make_backup(const char *fname, BOOL prefer_rename);
+void write_stream_flags(int fd);
+void read_stream_flags(int fd);
+void check_batch_flags(void);
+void write_batch_shell_file(int argc, char *argv[], int file_arg_cnt);
+uint32 get_checksum1(char *buf1, int32 len);
+void get_checksum2(char *buf, int32 len, char *sum);
+void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum);
+void sum_init(int seed);
+void sum_update(const char *p, int32 len);
+int sum_end(char *sum);
+struct chmod_mode_struct *parse_chmod(const char *modestr,
+ struct chmod_mode_struct **root_mode_ptr);
+int tweak_mode(int mode, struct chmod_mode_struct *chmod_modes);
+int free_chmod_mode(struct chmod_mode_struct *chmod_modes);
+void close_all(void);
+NORETURN void _exit_cleanup(int code, const char *file, int line);
+void cleanup_disable(void);
+void cleanup_set(const char *fnametmp, const char *fname, struct file_struct *file,
+ int fd_r, int fd_w);
+void cleanup_set_pid(pid_t pid);
+char *client_addr(int fd);
+char *client_name(int fd);
+void client_sockaddr(int fd,
+ struct sockaddr_storage *ss,
+ socklen_t *ss_len);
+int lookup_name(int fd, const struct sockaddr_storage *ss,
+ socklen_t ss_len,
+ char *name_buf, size_t name_buf_size,
+ char *port_buf, size_t port_buf_size);
+int compare_addrinfo_sockaddr(const struct addrinfo *ai,
+ const struct sockaddr_storage *ss);
+int check_name(int fd,
+ const struct sockaddr_storage *ss,
+ char *name_buf, size_t name_buf_size);
+int start_socket_client(char *host, int remote_argc, char *remote_argv[],
+ int argc, char *argv[]);
+int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char *argv[]);
+int start_daemon(int f_in, int f_out);
+int daemon_main(void);
+void set_allow_inc_recurse(void);
+void setup_protocol(int f_out,int f_in);
+int claim_connection(char *fname, int max_connections);
+enum delret delete_item(char *fbuf, uint16 mode, uint16 flags);
+uint16 get_del_for_flag(uint16 mode);
+void set_filter_dir(const char *dir, unsigned int dirlen);
+void *push_local_filters(const char *dir, unsigned int dirlen);
+void pop_local_filters(void *mem);
+void change_local_filter_dir(const char *dname, int dlen, int dir_depth);
+int check_filter(filter_rule_list *listp, enum logcode code,
+ const char *name, int name_is_dir);
+const filter_rule *rule_template(uint32 rflags);
+void parse_filter_str(filter_rule_list *listp, const char *rulestr,
+ const filter_rule *template, int xflags);
+void parse_filter_file(filter_rule_list *listp, const char *fname, const filter_rule *template, int xflags);
+char *get_rule_prefix(filter_rule *rule, const char *pat, int for_xfer,
+ unsigned int *plen_ptr);
+void send_filter_list(int f_out);
+void recv_filter_list(int f_in);
+int sparse_end(int f, OFF_T size);
+int flush_write_file(int f);
+int write_file(int f, char *buf, int len);
+struct map_struct *map_file(int fd, OFF_T len, int32 read_size, int32 blk_size);
+char *map_ptr(struct map_struct *map, OFF_T offset, int32 len);
+int unmap_file(struct map_struct *map);
+void init_flist(void);
+void show_flist_stats(void);
+int link_stat(const char *path, STRUCT_STAT *stp, int follow_dirlinks);
+int change_pathname(struct file_struct *file, const char *dir, int dirlen);
+struct file_struct *make_file(const char *fname, struct file_list *flist,
+ STRUCT_STAT *stp, int flags, int filter_level);
+void unmake_file(struct file_struct *file);
+void send_extra_file_list(int f, int at_least);
+struct file_list *send_file_list(int f, int argc, char *argv[]);
+struct file_list *recv_file_list(int f);
+void recv_additional_file_list(int f);
+int flist_find(struct file_list *flist, struct file_struct *f);
+int flist_find_ignore_dirness(struct file_list *flist, struct file_struct *f);
+void clear_file(struct file_struct *file);
+struct file_list *flist_new(int flags, char *msg);
+void flist_free(struct file_list *flist);
+int f_name_cmp(const struct file_struct *f1, const struct file_struct *f2);
+int f_name_has_prefix(const struct file_struct *f1, const struct file_struct *f2);
+char *f_name_buf(void);
+char *f_name(const struct file_struct *f, char *fbuf);
+struct file_list *get_dirlist(char *dirname, int dlen, int flags);
+int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp);
+void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statret,
+ stat_x *sxp, int32 iflags, uchar fnamecmp_type,
+ const char *xname);
+int unchanged_file(char *fn, struct file_struct *file, STRUCT_STAT *st);
+int atomic_create(struct file_struct *file, char *fname, const char *slnk, const char *hlnk,
+ dev_t rdev, stat_x *sxp, int del_for_flag);
+void check_for_finished_files(int itemizing, enum logcode code, int check_redo);
+void generate_files(int f_out, const char *local_name);
+struct hashtable *hashtable_create(int size, int key64);
+void hashtable_destroy(struct hashtable *tbl);
+void *hashtable_find(struct hashtable *tbl, int64 key, int allocate_if_missing);
+void init_hard_links(void);
+struct ht_int64_node *idev_find(int64 dev, int64 ino);
+void idev_destroy(void);
+void match_hard_links(struct file_list *flist);
+int hard_link_check(struct file_struct *file, int ndx, char *fname,
+ int statret, stat_x *sxp, int itemizing,
+ enum logcode code);
+int hard_link_one(struct file_struct *file, const char *fname,
+ const char *oldname, int terse);
+void finish_hard_link(struct file_struct *file, const char *fname, int fin_ndx,
+ STRUCT_STAT *stp, int itemizing, enum logcode code,
+ int alt_dest);
+int skip_hard_link(struct file_struct *file, struct file_list **flist_p);
+void reduce_iobuf_size(xbuf *out, size_t new_size);
+void restore_iobuf_size(xbuf *out);
+void noop_io_until_death(void);
+int send_msg(enum msgcode code, const char *buf, size_t len, int convert);
+void send_msg_int(enum msgcode code, int num);
+void io_set_sock_fds(int f_in, int f_out);
+void set_io_timeout(int secs);
+void increment_active_files(int ndx, int itemizing, enum logcode code);
+int get_redo_num(void);
+int get_hlink_num(void);
+void start_filesfrom_forwarding(int fd);
+int read_line(int fd, char *buf, size_t bufsiz, int flags);
+void read_args(int f_in, char *mod_name, char *buf, size_t bufsiz, int rl_nulls,
+ char ***argv_p, int *argc_p, char **request_p);
+BOOL io_start_buffering_out(int f_out);
+BOOL io_start_buffering_in(int f_in);
+void io_end_buffering_in(BOOL free_buffers);
+void io_end_buffering_out(BOOL free_buffers);
+void maybe_flush_socket(int important);
+void maybe_send_keepalive(time_t now, int flags);
+void start_flist_forward(int ndx);
+void stop_flist_forward(void);
+void wait_for_receiver(void);
+unsigned short read_shortint(int f);
+int32 read_int(int f);
+int32 read_varint(int f);
+int64 read_varlong(int f, uchar min_bytes);
+int64 read_longint(int f);
+void read_buf(int f, char *buf, size_t len);
+void read_sbuf(int f, char *buf, size_t len);
+uchar read_byte(int f);
+int read_vstring(int f, char *buf, int bufsize);
+void read_sum_head(int f, struct sum_struct *sum);
+void write_sum_head(int f, struct sum_struct *sum);
+void io_flush(int flush_it_all);
+void write_shortint(int f, unsigned short x);
+void write_int(int f, int32 x);
+void write_varint(int f, int32 x);
+void write_varlong(int f, int64 x, uchar min_bytes);
+void write_longint(int f, int64 x);
+void write_bigbuf(int f, const char *buf, size_t len);
+void write_buf(int f, const char *buf, size_t len);
+void write_sbuf(int f, const char *buf);
+void write_byte(int f, uchar c);
+void write_vstring(int f, const char *str, int len);
+void write_ndx(int f, int32 ndx);
+int32 read_ndx(int f);
+int read_line_old(int fd, char *buf, size_t bufsiz, int eof_ok);
+void io_printf(int fd, const char *format, ...);
+void io_start_multiplex_out(int fd);
+void io_start_multiplex_in(int fd);
+int io_end_multiplex_in(int mode);
+int io_end_multiplex_out(int mode);
+void start_write_batch(int fd);
+void stop_write_batch(void);
+char *lp_bind_address(void);
+char *lp_motd_file(void);
+char *lp_pid_file(void);
+char *lp_socket_options(void);
+int lp_listen_backlog(void);
+int lp_rsync_port(void);
+char *lp_auth_users(int module_id);
+char *lp_charset(int module_id);
+char *lp_comment(int module_id);
+char *lp_dont_compress(int module_id);
+char *lp_exclude(int module_id);
+char *lp_exclude_from(int module_id);
+char *lp_filter(int module_id);
+char *lp_gid(int module_id);
+char *lp_hosts_allow(int module_id);
+char *lp_hosts_deny(int module_id);
+char *lp_include(int module_id);
+char *lp_include_from(int module_id);
+char *lp_incoming_chmod(int module_id);
+char *lp_lock_file(int module_id);
+char *lp_log_file(int module_id);
+char *lp_log_format(int module_id);
+char *lp_name(int module_id);
+char *lp_outgoing_chmod(int module_id);
+char *lp_path(int module_id);
+char *lp_postxfer_exec(int module_id);
+char *lp_prexfer_exec(int module_id);
+char *lp_refuse_options(int module_id);
+char *lp_secrets_file(int module_id);
+char *lp_temp_dir(int module_id);
+char *lp_uid(int module_id);
+int lp_max_connections(int module_id);
+int lp_max_verbosity(int module_id);
+int lp_syslog_facility(int module_id);
+int lp_timeout(int module_id);
+BOOL lp_fake_super(int module_id);
+BOOL lp_forward_lookup(int module_id);
+BOOL lp_ignore_errors(int module_id);
+BOOL lp_ignore_nonreadable(int module_id);
+BOOL lp_list(int module_id);
+BOOL lp_munge_symlinks(int module_id);
+BOOL lp_numeric_ids(int module_id);
+BOOL lp_read_only(int module_id);
+BOOL lp_reverse_lookup(int module_id);
+BOOL lp_strict_modes(int module_id);
+BOOL lp_transfer_logging(int module_id);
+BOOL lp_use_chroot(int module_id);
+BOOL lp_write_only(int module_id);
+int lp_load(char *pszFname, int globals_only);
+BOOL set_dparams(int syntax_check_only);
+int lp_num_modules(void);
+int lp_number(char *name);
+void log_init(int restart);
+void logfile_close(void);
+void logfile_reopen(void);
+void rwrite(enum logcode code, const char *buf, int len, int is_utf8);
+void rprintf(enum logcode code, const char *format, ...);
+void rsyserr(enum logcode code, int errcode, const char *format, ...);
+void rflush(enum logcode code);
+void remember_initial_stats(void);
+int log_format_has(const char *format, char esc);
+void log_item(enum logcode code, struct file_struct *file, int iflags, const char *hlink);
+void maybe_log_item(struct file_struct *file, int iflags, int itemizing,
+ const char *buf);
+void log_delete(const char *fname, int mode);
+void log_exit(int code, const char *file, int line);
+pid_t wait_process(pid_t pid, int *status_ptr, int flags);
+void write_del_stats(int f);
+void read_del_stats(int f);
+int child_main(int argc, char *argv[]);
+void start_server(int f_in, int f_out, int argc, char *argv[]);
+int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[]);
+RETSIGTYPE remember_children(UNUSED(int val));
+const char *get_panic_action(void);
+int main(int argc,char *argv[]);
+void match_sums(int f, struct sum_struct *s, struct map_struct *buf, OFF_T len);
+void match_report(void);
+void limit_output_verbosity(int level);
+void reset_output_levels(void);
+void negate_output_levels(void);
+void usage(enum logcode F);
+void option_error(void);
+int parse_arguments(int *argc_p, const char ***argv_p);
+void server_options(char **args, int *argc_p);
+char *check_for_hostspec(char *s, char **host_ptr, int *port_ptr);
+int pm_process( char *FileName,
+ BOOL (*sfunc)(char *),
+ BOOL (*pfunc)(char *, char *) );
+pid_t piped_child(char **command, int *f_in, int *f_out);
+pid_t local_child(int argc, char **argv, int *f_in, int *f_out,
+ int (*child_main)(int, char*[]));
+void set_current_file_index(struct file_struct *file, int ndx);
+void end_progress(OFF_T size);
+void show_progress(OFF_T ofs, OFF_T size);
+int get_tmpname(char *fnametmp, const char *fname, BOOL make_unique);
+int open_tmpfile(char *fnametmp, const char *fname, struct file_struct *file);
+int recv_files(int f_in, int f_out, char *local_name);
+void setup_iconv(void);
+int iconvbufs(iconv_t ic, xbuf *in, xbuf *out, int flags);
+void send_protected_args(int fd, char *args[]);
+int read_ndx_and_attrs(int f_in, int f_out, int *iflag_ptr, uchar *type_ptr,
+ char *buf, int *len_ptr);
+void free_sums(struct sum_struct *s);
+mode_t dest_mode(mode_t flist_mode, mode_t stat_mode, int dflt_perms,
+ int exists);
+int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
+ const char *fnamecmp, int flags);
+RETSIGTYPE sig_int(int sig_num);
+int finish_transfer(const char *fname, const char *fnametmp,
+ const char *fnamecmp, const char *partialptr,
+ struct file_struct *file, int ok_to_set_time,
+ int overwriting_basis);
+struct file_list *flist_for_ndx(int ndx, const char *fatal_error_loc);
+const char *who_am_i(void);
+void successful_send(int ndx);
+void send_files(int f_in, int f_out);
+int try_bind_local(int s, int ai_family, int ai_socktype,
+ const char *bind_addr);
+int open_socket_out(char *host, int port, const char *bind_addr,
+ int af_hint);
+int open_socket_out_wrapped(char *host, int port, const char *bind_addr,
+ int af_hint);
+int is_a_socket(int fd);
+void start_accept_loop(int port, int (*fn)(int, int));
+void set_socket_options(int fd, char *options);
+int do_unlink(const char *fname);
+int do_symlink(const char *lnk, const char *fname);
+ssize_t do_readlink(const char *path, char *buf, size_t bufsiz);
+int do_link(const char *fname1, const char *fname2);
+int do_lchown(const char *path, uid_t owner, gid_t group);
+int do_mknod(const char *pathname, mode_t mode, dev_t dev);
+int do_rmdir(const char *pathname);
+int do_open(const char *pathname, int flags, mode_t mode);
+int do_chmod(const char *path, mode_t mode);
+int do_rename(const char *fname1, const char *fname2);
+int do_ftruncate(int fd, OFF_T size);
+void trim_trailing_slashes(char *name);
+int do_mkdir(char *fname, mode_t mode);
+int do_mkstemp(char *template, mode_t perms);
+int do_stat(const char *fname, STRUCT_STAT *st);
+int do_lstat(const char *fname, STRUCT_STAT *st);
+int do_fstat(int fd, STRUCT_STAT *st);
+OFF_T do_lseek(int fd, OFF_T offset, int whence);
+int do_utimensat(const char *fname, time_t modtime, uint32 mod_nsec);
+int do_lutimes(const char *fname, time_t modtime, uint32 mod_nsec);
+int do_utimes(const char *fname, time_t modtime, uint32 mod_nsec);
+int do_utime(const char *fname, time_t modtime, UNUSED(uint32 mod_nsec));
+int do_fallocate(int fd, OFF_T offset, OFF_T length);
+int do_open_nofollow(const char *pathname, int flags);
+void set_compression(const char *fname);
+void send_token(int f, int32 token, struct map_struct *buf, OFF_T offset,
+ int32 n, int32 toklen);
+int32 recv_token(int f, char **data);
+void see_token(char *data, int32 toklen);
+char *uid_to_user(uid_t uid);
+char *gid_to_group(gid_t gid);
+int user_to_uid(const char *name, uid_t *uid_p, BOOL num_ok);
+int group_to_gid(const char *name, gid_t *gid_p, BOOL num_ok);
+uid_t match_uid(uid_t uid);
+gid_t match_gid(gid_t gid, uint16 *flags_ptr);
+const char *add_uid(uid_t uid);
+const char *add_gid(gid_t gid);
+void send_id_list(int f);
+uid_t recv_user_name(int f, uid_t uid);
+gid_t recv_group_name(int f, gid_t gid, uint16 *flags_ptr);
+void recv_id_list(int f, struct file_list *flist);
+void parse_name_map(char *map, BOOL usernames);
+const char *getallgroups(uid_t uid, gid_t *gid_list, int *size_ptr);
+void set_nonblocking(int fd);
+void set_blocking(int fd);
+int fd_pair(int fd[2]);
+void print_child_argv(const char *prefix, char **cmd);
+int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode);
+int make_path(char *fname, int flags);
+int full_write(int desc, const char *ptr, size_t len);
+int copy_file(const char *source, const char *dest, int ofd, mode_t mode);
+int robust_unlink(const char *fname);
+int robust_rename(const char *from, const char *to, const char *partialptr,
+ int mode);
+pid_t do_fork(void);
+void kill_all(int sig);
+int lock_range(int fd, int offset, int len);
+int glob_expand(const char *arg, char ***argv_p, int *argc_p, int *maxargs_p);
+void glob_expand_module(char *base1, char *arg, char ***argv_p, int *argc_p, int *maxargs_p);
+void strlower(char *s);
+size_t pathjoin(char *dest, size_t destsize, const char *p1, const char *p2);
+size_t stringjoin(char *dest, size_t destsize, ...);
+int count_dir_elements(const char *p);
+int clean_fname(char *name, int flags);
+char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth,
+ int flags);
+int change_dir(const char *dir, int set_path_only);
+char *normalize_path(char *path, BOOL force_newbuf, unsigned int *len_ptr);
+char *full_fname(const char *fn);
+char *partial_dir_fname(const char *fname);
+int handle_partial_dir(const char *fname, int create);
+int unsafe_symlink(const char *dest, const char *src);
+char *timestring(time_t t);
+int cmp_time(time_t file1, time_t file2);
+int _Insure_trap_error(int a1, int a2, int a3, int a4, int a5, int a6);
+const char *find_filename_suffix(const char *fn, int fn_len, int *len_ptr);
+uint32 fuzzy_distance(const char *s1, unsigned len1, const char *s2, unsigned len2);
+struct bitbag *bitbag_create(int max_ndx);
+void bitbag_set_bit(struct bitbag *bb, int ndx);
+void bitbag_clear_bit(struct bitbag *bb, int ndx);
+int bitbag_check_bit(struct bitbag *bb, int ndx);
+int bitbag_next_bit(struct bitbag *bb, int after);
+void flist_ndx_push(flist_ndx_list *lp, int ndx);
+int flist_ndx_pop(flist_ndx_list *lp);
+void *expand_item_list(item_list *lp, size_t item_size,
+ const char *desc, int incr);
+int msleep(int t);
+void *_new_array(unsigned long num, unsigned int size, int use_calloc);
+void *_realloc_array(void *ptr, unsigned int size, size_t num);
+const char *sum_as_hex(const char *sum);
+NORETURN void out_of_memory(const char *str);
+NORETURN void overflow_exit(const char *str);
+void free_xattr(stat_x *sxp);
+int get_xattr(const char *fname, stat_x *sxp);
+int copy_xattrs(const char *source, const char *dest);
+int send_xattr(int f, stat_x *sxp);
+int xattr_diff(struct file_struct *file, stat_x *sxp, int find_all);
+void send_xattr_request(const char *fname, struct file_struct *file, int f_out);
+int recv_xattr_request(struct file_struct *file, int f_in);
+void receive_xattr(int f, struct file_struct *file);
+void cache_tmp_xattr(struct file_struct *file, stat_x *sxp);
+void uncache_tmp_xattrs(void);
+int set_xattr(const char *fname, const struct file_struct *file,
+ const char *fnamecmp, stat_x *sxp);
+char *get_xattr_acl(const char *fname, int is_access_acl, size_t *len_p);
+int set_xattr_acl(const char *fname, int is_access_acl, const char *buf, size_t buf_len);
+int del_def_xattr_acl(const char *fname);
+int get_stat_xattr(const char *fname, int fd, STRUCT_STAT *fst, STRUCT_STAT *xst);
+int set_stat_xattr(const char *fname, struct file_struct *file, mode_t new_mode);
+int x_stat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst);
+int x_lstat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst);
+int x_fstat(int fd, STRUCT_STAT *fst, STRUCT_STAT *xst);
+int sys_gettimeofday(struct timeval *tv);
+char *do_big_num(int64 num, int human_flag, const char *fract);
+char *do_big_dnum(double dnum, int human_flag, int decimal_digits);
diff --git a/rsync/proto.h-tstamp b/rsync/proto.h-tstamp
new file mode 100644
index 0000000..e69de29
diff --git a/rsync/receiver.c b/rsync/receiver.c
new file mode 100644
index 0000000..571b7da
--- /dev/null
+++ b/rsync/receiver.c
@@ -0,0 +1,953 @@
+/*
+ * Routines only used by the receiving process.
+ *
+ * Copyright (C) 1996-2000 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2003-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "inums.h"
+
+extern int dry_run;
+extern int do_xfers;
+extern int am_root;
+extern int am_server;
+extern int inc_recurse;
+extern int log_before_transfer;
+extern int stdout_format_has_i;
+extern int logfile_format_has_i;
+extern int want_xattr_optim;
+extern int csum_length;
+extern int read_batch;
+extern int write_batch;
+extern int batch_gen_fd;
+extern int protocol_version;
+extern int relative_paths;
+extern int preserve_hard_links;
+extern int preserve_perms;
+extern int preserve_xattrs;
+extern int basis_dir_cnt;
+extern int make_backups;
+extern int cleanup_got_literal;
+extern int remove_source_files;
+extern int append_mode;
+extern int sparse_files;
+extern int preallocate_files;
+extern int keep_partial;
+extern int checksum_len;
+extern int checksum_seed;
+extern int inplace;
+extern int allowed_lull;
+extern int delay_updates;
+extern mode_t orig_umask;
+extern struct stats stats;
+extern char *tmpdir;
+extern char *partial_dir;
+extern char *basis_dir[MAX_BASIS_DIRS+1];
+extern char sender_file_sum[MAX_DIGEST_LEN];
+extern struct file_list *cur_flist, *first_flist, *dir_flist;
+extern filter_rule_list daemon_filter_list;
+
+static struct bitbag *delayed_bits = NULL;
+static int phase = 0, redoing = 0;
+static flist_ndx_list batch_redo_list;
+/* We're either updating the basis file or an identical copy: */
+static int updating_basis_or_equiv;
+
+#define TMPNAME_SUFFIX ".XXXXXX"
+#define TMPNAME_SUFFIX_LEN ((int)sizeof TMPNAME_SUFFIX - 1)
+#define MAX_UNIQUE_NUMBER 999999
+#define MAX_UNIQUE_LOOP 100
+
+/* get_tmpname() - create a tmp filename for a given filename
+ *
+ * If a tmpdir is defined, use that as the directory to put it in. Otherwise,
+ * the tmp filename is in the same directory as the given name. Note that
+ * there may be no directory at all in the given name!
+ *
+ * The tmp filename is basically the given filename with a dot prepended, and
+ * .XXXXXX appended (for mkstemp() to put its unique gunk in). We take care
+ * to not exceed either the MAXPATHLEN or NAME_MAX, especially the last, as
+ * the basename basically becomes 8 characters longer. In such a case, the
+ * original name is shortened sufficiently to make it all fit.
+ *
+ * If the make_unique arg is True, the XXXXXX string is replaced with a unique
+ * string that doesn't exist at the time of the check. This is intended to be
+ * used for creating hard links, symlinks, devices, and special files, since
+ * normal files should be handled by mkstemp() for safety.
+ *
+ * Of course, the only reason the file is based on the original name is to
+ * make it easier to figure out what purpose a temp file is serving when a
+ * transfer is in progress. */
+int get_tmpname(char *fnametmp, const char *fname, BOOL make_unique)
+{
+ int maxname, length = 0;
+ const char *f;
+ char *suf;
+
+ if (tmpdir) {
+ /* Note: this can't overflow, so the return value is safe */
+ length = strlcpy(fnametmp, tmpdir, MAXPATHLEN - 2);
+ fnametmp[length++] = '/';
+ }
+
+ if ((f = strrchr(fname, '/')) != NULL) {
+ ++f;
+ if (!tmpdir) {
+ length = f - fname;
+ /* copy up to and including the slash */
+ strlcpy(fnametmp, fname, length + 1);
+ }
+ } else
+ f = fname;
+
+ if (!tmpdir) { /* using a tmpdir avoids the leading dot on our temp names */
+ if (*f == '.') /* avoid an extra leading dot for OS X's sake */
+ f++;
+ fnametmp[length++] = '.';
+ }
+
+ /* The maxname value is bufsize, and includes space for the '\0'.
+ * NAME_MAX needs an extra -1 for the name's leading dot. */
+ maxname = MIN(MAXPATHLEN - length - TMPNAME_SUFFIX_LEN,
+ NAME_MAX - 1 - TMPNAME_SUFFIX_LEN);
+
+ if (maxname < 0) {
+ rprintf(FERROR_XFER, "temporary filename too long: %s\n", fname);
+ fnametmp[0] = '\0';
+ return 0;
+ }
+
+ if (maxname) {
+ int added = strlcpy(fnametmp + length, f, maxname);
+ if (added >= maxname)
+ added = maxname - 1;
+ suf = fnametmp + length + added;
+
+ /* Trim any dangling high-bit chars if the first-trimmed char (if any) is
+ * also a high-bit char, just in case we cut into a multi-byte sequence.
+ * We are guaranteed to stop because of the leading '.' we added. */
+ if ((int)f[added] & 0x80) {
+ while ((int)suf[-1] & 0x80)
+ suf--;
+ }
+ /* trim one trailing dot before our suffix's dot */
+ if (suf[-1] == '.')
+ suf--;
+ } else
+ suf = fnametmp + length - 1; /* overwrite the leading dot with suffix's dot */
+
+ if (make_unique) {
+ static unsigned counter_limit;
+ unsigned counter;
+
+ if (!counter_limit) {
+ counter_limit = (unsigned)getpid() + MAX_UNIQUE_LOOP;
+ if (counter_limit > MAX_UNIQUE_NUMBER || counter_limit < MAX_UNIQUE_LOOP)
+ counter_limit = MAX_UNIQUE_LOOP;
+ }
+ counter = counter_limit - MAX_UNIQUE_LOOP;
+
+ /* This doesn't have to be very good because we don't need
+ * to worry about someone trying to guess the values: all
+ * a conflict will do is cause a device, special file, hard
+ * link, or symlink to fail to be created. Also: avoid
+ * using mktemp() due to gcc's annoying warning. */
+ while (1) {
+ snprintf(suf, TMPNAME_SUFFIX_LEN+1, ".%d", counter);
+ if (access(fnametmp, 0) < 0)
+ break;
+ if (++counter >= counter_limit)
+ return 0;
+ }
+ } else
+ memcpy(suf, TMPNAME_SUFFIX, TMPNAME_SUFFIX_LEN+1);
+
+ return 1;
+}
+
+/* Opens a temporary file for writing.
+ * Success: Writes name into fnametmp, returns fd.
+ * Failure: Clobbers fnametmp, returns -1.
+ * Calling cleanup_set() is the caller's job. */
+int open_tmpfile(char *fnametmp, const char *fname, struct file_struct *file)
+{
+ int fd;
+ mode_t added_perms;
+
+ if (!get_tmpname(fnametmp, fname, False))
+ return -1;
+
+ if (am_root < 0) {
+ /* For --fake-super, the file must be useable by the copying
+ * user, just like it would be for root. */
+ added_perms = S_IRUSR|S_IWUSR;
+ } else {
+ /* For a normal copy, we need to be able to tweak things like xattrs. */
+ added_perms = S_IWUSR;
+ }
+
+ /* We initially set the perms without the setuid/setgid bits or group
+ * access to ensure that there is no race condition. They will be
+ * correctly updated after the right owner and group info is set.
+ * (Thanks to snabb@epipe.fi for pointing this out.) */
+ fd = do_mkstemp(fnametmp, (file->mode|added_perms) & INITACCESSPERMS);
+
+#if 0
+ /* In most cases parent directories will already exist because their
+ * information should have been previously transferred, but that may
+ * not be the case with -R */
+ if (fd == -1 && relative_paths && errno == ENOENT
+ && make_path(fnametmp, MKP_SKIP_SLASH | MKP_DROP_NAME) == 0) {
+ /* Get back to name with XXXXXX in it. */
+ get_tmpname(fnametmp, fname, False);
+ fd = do_mkstemp(fnametmp, (file->mode|added_perms) & INITACCESSPERMS);
+ }
+#endif
+
+ if (fd == -1) {
+ rsyserr(FERROR_XFER, errno, "mkstemp %s failed",
+ full_fname(fnametmp));
+ return -1;
+ }
+
+ return fd;
+}
+
+static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
+ const char *fname, int fd, OFF_T total_size)
+{
+ static char file_sum1[MAX_DIGEST_LEN];
+ struct map_struct *mapbuf;
+ struct sum_struct sum;
+ int32 len;
+ OFF_T offset = 0;
+ OFF_T offset2;
+ char *data;
+ int32 i;
+ char *map = NULL;
+#ifdef SUPPORT_PREALLOCATION
+#ifdef PREALLOCATE_NEEDS_TRUNCATE
+ OFF_T preallocated_len = 0;
+#endif
+
+ if (preallocate_files && fd != -1 && total_size > 0 && (!inplace || total_size > size_r)) {
+ /* Try to preallocate enough space for file's eventual length. Can
+ * reduce fragmentation on filesystems like ext4, xfs, and NTFS. */
+ if (do_fallocate(fd, 0, total_size) == 0) {
+#ifdef PREALLOCATE_NEEDS_TRUNCATE
+ preallocated_len = total_size;
+#endif
+ } else
+ rsyserr(FWARNING, errno, "do_fallocate %s", full_fname(fname));
+ }
+#endif
+
+ read_sum_head(f_in, &sum);
+
+ if (fd_r >= 0 && size_r > 0) {
+ int32 read_size = MAX(sum.blength * 2, 16*1024);
+ mapbuf = map_file(fd_r, size_r, read_size, sum.blength);
+ if (DEBUG_GTE(DELTASUM, 2)) {
+ rprintf(FINFO, "recv mapped %s of size %s\n",
+ fname_r, big_num(size_r));
+ }
+ } else
+ mapbuf = NULL;
+
+ sum_init(checksum_seed);
+
+ if (append_mode > 0) {
+ OFF_T j;
+ sum.flength = (OFF_T)sum.count * sum.blength;
+ if (sum.remainder)
+ sum.flength -= sum.blength - sum.remainder;
+ if (append_mode == 2 && mapbuf) {
+ for (j = CHUNK_SIZE; j < sum.flength; j += CHUNK_SIZE) {
+ if (INFO_GTE(PROGRESS, 1))
+ show_progress(offset, total_size);
+ sum_update(map_ptr(mapbuf, offset, CHUNK_SIZE),
+ CHUNK_SIZE);
+ offset = j;
+ }
+ if (offset < sum.flength) {
+ int32 len = (int32)(sum.flength - offset);
+ if (INFO_GTE(PROGRESS, 1))
+ show_progress(offset, total_size);
+ sum_update(map_ptr(mapbuf, offset, len), len);
+ }
+ }
+ offset = sum.flength;
+ if (fd != -1 && (j = do_lseek(fd, offset, SEEK_SET)) != offset) {
+ rsyserr(FERROR_XFER, errno, "lseek of %s returned %s, not %s",
+ full_fname(fname), big_num(j), big_num(offset));
+ exit_cleanup(RERR_FILEIO);
+ }
+ }
+
+ while ((i = recv_token(f_in, &data)) != 0) {
+ if (INFO_GTE(PROGRESS, 1))
+ show_progress(offset, total_size);
+
+ if (allowed_lull)
+ maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH | MSK_ACTIVE_RECEIVER);
+
+ if (i > 0) {
+ if (DEBUG_GTE(DELTASUM, 3)) {
+ rprintf(FINFO,"data recv %d at %s\n",
+ i, big_num(offset));
+ }
+
+ stats.literal_data += i;
+ cleanup_got_literal = 1;
+
+ sum_update(data, i);
+
+ if (fd != -1 && write_file(fd,data,i) != i)
+ goto report_write_error;
+ offset += i;
+ continue;
+ }
+
+ i = -(i+1);
+ offset2 = i * (OFF_T)sum.blength;
+ len = sum.blength;
+ if (i == (int)sum.count-1 && sum.remainder != 0)
+ len = sum.remainder;
+
+ stats.matched_data += len;
+
+ if (DEBUG_GTE(DELTASUM, 3)) {
+ rprintf(FINFO,
+ "chunk[%d] of size %ld at %s offset=%s%s\n",
+ i, (long)len, big_num(offset2), big_num(offset),
+ updating_basis_or_equiv && offset == offset2 ? " (seek)" : "");
+ }
+
+ if (mapbuf) {
+ map = map_ptr(mapbuf,offset2,len);
+
+ see_token(map, len);
+ sum_update(map, len);
+ }
+
+ if (updating_basis_or_equiv) {
+ if (offset == offset2 && fd != -1) {
+ OFF_T pos;
+ if (flush_write_file(fd) < 0)
+ goto report_write_error;
+ offset += len;
+ if ((pos = do_lseek(fd, len, SEEK_CUR)) != offset) {
+ rsyserr(FERROR_XFER, errno,
+ "lseek of %s returned %s, not %s",
+ full_fname(fname),
+ big_num(pos), big_num(offset));
+ exit_cleanup(RERR_FILEIO);
+ }
+ continue;
+ }
+ }
+ if (fd != -1 && map && write_file(fd, map, len) != (int)len)
+ goto report_write_error;
+ offset += len;
+ }
+
+ if (flush_write_file(fd) < 0)
+ goto report_write_error;
+
+#ifdef HAVE_FTRUNCATE
+ /* inplace: New data could be shorter than old data.
+ * preallocate_files: total_size could have been an overestimate.
+ * Cut off any extra preallocated zeros from dest file. */
+ if ((inplace
+#ifdef PREALLOCATE_NEEDS_TRUNCATE
+ || preallocated_len > offset
+#endif
+ ) && fd != -1 && do_ftruncate(fd, offset) < 0) {
+ rsyserr(FERROR_XFER, errno, "ftruncate failed on %s",
+ full_fname(fname));
+ }
+#endif
+
+ if (INFO_GTE(PROGRESS, 1))
+ end_progress(total_size);
+
+ if (fd != -1 && offset > 0 && sparse_end(fd, offset) != 0) {
+ report_write_error:
+ rsyserr(FERROR_XFER, errno, "write failed on %s",
+ full_fname(fname));
+ exit_cleanup(RERR_FILEIO);
+ }
+
+ if (sum_end(file_sum1) != checksum_len)
+ overflow_exit("checksum_len"); /* Impossible... */
+
+ if (mapbuf)
+ unmap_file(mapbuf);
+
+ read_buf(f_in, sender_file_sum, checksum_len);
+ if (DEBUG_GTE(DELTASUM, 2))
+ rprintf(FINFO,"got file_sum\n");
+ if (fd != -1 && memcmp(file_sum1, sender_file_sum, checksum_len) != 0)
+ return 0;
+ return 1;
+}
+
+
+static void discard_receive_data(int f_in, OFF_T length)
+{
+ receive_data(f_in, NULL, -1, 0, NULL, -1, length);
+}
+
+static void handle_delayed_updates(char *local_name)
+{
+ char *fname, *partialptr;
+ int ndx;
+
+ for (ndx = -1; (ndx = bitbag_next_bit(delayed_bits, ndx)) >= 0; ) {
+ struct file_struct *file = cur_flist->files[ndx];
+ fname = local_name ? local_name : f_name(file, NULL);
+ if ((partialptr = partial_dir_fname(fname)) != NULL) {
+ if (make_backups > 0 && !make_backup(fname, False))
+ continue;
+ if (DEBUG_GTE(RECV, 1)) {
+ rprintf(FINFO, "renaming %s to %s\n",
+ partialptr, fname);
+ }
+ /* We don't use robust_rename() here because the
+ * partial-dir must be on the same drive. */
+ if (do_rename(partialptr, fname) < 0) {
+ rsyserr(FERROR_XFER, errno,
+ "rename failed for %s (from %s)",
+ full_fname(fname), partialptr);
+ } else {
+ if (remove_source_files
+ || (preserve_hard_links && F_IS_HLINKED(file)))
+ send_msg_int(MSG_SUCCESS, ndx);
+ handle_partial_dir(partialptr, PDIR_DELETE);
+ }
+ }
+ }
+}
+
+static void no_batched_update(int ndx, BOOL is_redo)
+{
+ struct file_list *flist = flist_for_ndx(ndx, "no_batched_update");
+ struct file_struct *file = flist->files[ndx - flist->ndx_start];
+
+ rprintf(FERROR_XFER, "(No batched update for%s \"%s\")\n",
+ is_redo ? " resend of" : "", f_name(file, NULL));
+
+ if (inc_recurse && !dry_run)
+ send_msg_int(MSG_NO_SEND, ndx);
+}
+
+static int we_want_redo(int desired_ndx)
+{
+ static int redo_ndx = -1;
+
+ while (redo_ndx < desired_ndx) {
+ if (redo_ndx >= 0)
+ no_batched_update(redo_ndx, True);
+ if ((redo_ndx = flist_ndx_pop(&batch_redo_list)) < 0)
+ return 0;
+ }
+
+ if (redo_ndx == desired_ndx) {
+ redo_ndx = -1;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int gen_wants_ndx(int desired_ndx, int flist_num)
+{
+ static int next_ndx = -1;
+ static int done_cnt = 0;
+ static BOOL got_eof = False;
+
+ if (got_eof)
+ return 0;
+
+ /* TODO: integrate gen-reading I/O into perform_io() so this is not needed? */
+ io_flush(FULL_FLUSH);
+
+ while (next_ndx < desired_ndx) {
+ if (inc_recurse && flist_num <= done_cnt)
+ return 0;
+ if (next_ndx >= 0)
+ no_batched_update(next_ndx, False);
+ if ((next_ndx = read_int(batch_gen_fd)) < 0) {
+ if (inc_recurse) {
+ done_cnt++;
+ continue;
+ }
+ got_eof = True;
+ return 0;
+ }
+ }
+
+ if (next_ndx == desired_ndx) {
+ next_ndx = -1;
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * main routine for receiver process.
+ *
+ * Receiver process runs on the same host as the generator process. */
+int recv_files(int f_in, int f_out, char *local_name)
+{
+ int fd1,fd2;
+ STRUCT_STAT st;
+ int iflags, xlen;
+ char *fname, fbuf[MAXPATHLEN];
+ char xname[MAXPATHLEN];
+ char fnametmp[MAXPATHLEN];
+ char *fnamecmp, *partialptr;
+ char fnamecmpbuf[MAXPATHLEN];
+ uchar fnamecmp_type;
+ struct file_struct *file;
+ int itemizing = am_server ? logfile_format_has_i : stdout_format_has_i;
+ enum logcode log_code = log_before_transfer ? FLOG : FINFO;
+ int max_phase = protocol_version >= 29 ? 2 : 1;
+ int dflt_perms = (ACCESSPERMS & ~orig_umask);
+#ifdef SUPPORT_ACLS
+ const char *parent_dirname = "";
+#endif
+ int ndx, recv_ok;
+
+ if (DEBUG_GTE(RECV, 1))
+ rprintf(FINFO, "recv_files(%d) starting\n", cur_flist->used);
+
+ if (delay_updates)
+ delayed_bits = bitbag_create(cur_flist->used + 1);
+
+ while (1) {
+ cleanup_disable();
+
+ /* This call also sets cur_flist. */
+ ndx = read_ndx_and_attrs(f_in, f_out, &iflags, &fnamecmp_type,
+ xname, &xlen);
+ if (ndx == NDX_DONE) {
+ if (!am_server && INFO_GTE(PROGRESS, 2) && cur_flist) {
+ set_current_file_index(NULL, 0);
+ end_progress(0);
+ }
+ if (inc_recurse && first_flist) {
+ if (read_batch) {
+ ndx = first_flist->used + first_flist->ndx_start;
+ gen_wants_ndx(ndx, first_flist->flist_num);
+ }
+ flist_free(first_flist);
+ if (first_flist)
+ continue;
+ } else if (read_batch && first_flist) {
+ ndx = first_flist->used;
+ gen_wants_ndx(ndx, first_flist->flist_num);
+ }
+ if (++phase > max_phase)
+ break;
+ if (DEBUG_GTE(RECV, 1))
+ rprintf(FINFO, "recv_files phase=%d\n", phase);
+ if (phase == 2 && delay_updates)
+ handle_delayed_updates(local_name);
+ write_int(f_out, NDX_DONE);
+ continue;
+ }
+
+ if (ndx - cur_flist->ndx_start >= 0)
+ file = cur_flist->files[ndx - cur_flist->ndx_start];
+ else
+ file = dir_flist->files[cur_flist->parent_ndx];
+ fname = local_name ? local_name : f_name(file, fbuf);
+
+ if (DEBUG_GTE(RECV, 1))
+ rprintf(FINFO, "recv_files(%s)\n", fname);
+
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs && iflags & ITEM_REPORT_XATTR && do_xfers
+ && !(want_xattr_optim && BITS_SET(iflags, ITEM_XNAME_FOLLOWS|ITEM_LOCAL_CHANGE)))
+ recv_xattr_request(file, f_in);
+#endif
+
+ if (!(iflags & ITEM_TRANSFER)) {
+ maybe_log_item(file, iflags, itemizing, xname);
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs && iflags & ITEM_REPORT_XATTR && do_xfers
+ && !BITS_SET(iflags, ITEM_XNAME_FOLLOWS|ITEM_LOCAL_CHANGE))
+ set_file_attrs(fname, file, NULL, fname, 0);
+#endif
+ if (iflags & ITEM_IS_NEW) {
+ stats.created_files++;
+ if (S_ISREG(file->mode)) {
+ /* Nothing further to count. */
+ } else if (S_ISDIR(file->mode))
+ stats.created_dirs++;
+#ifdef SUPPORT_LINKS
+ else if (S_ISLNK(file->mode))
+ stats.created_symlinks++;
+#endif
+ else if (IS_DEVICE(file->mode))
+ stats.created_devices++;
+ else
+ stats.created_specials++;
+ }
+ continue;
+ }
+ if (phase == 2) {
+ rprintf(FERROR,
+ "got transfer request in phase 2 [%s]\n",
+ who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ if (file->flags & FLAG_FILE_SENT) {
+ if (csum_length == SHORT_SUM_LENGTH) {
+ if (keep_partial && !partial_dir)
+ make_backups = -make_backups; /* prevents double backup */
+ if (append_mode)
+ sparse_files = -sparse_files;
+ append_mode = -append_mode;
+ csum_length = SUM_LENGTH;
+ redoing = 1;
+ }
+ } else {
+ if (csum_length != SHORT_SUM_LENGTH) {
+ if (keep_partial && !partial_dir)
+ make_backups = -make_backups;
+ if (append_mode)
+ sparse_files = -sparse_files;
+ append_mode = -append_mode;
+ csum_length = SHORT_SUM_LENGTH;
+ redoing = 0;
+ }
+ if (iflags & ITEM_IS_NEW)
+ stats.created_files++;
+ }
+
+ if (!am_server && INFO_GTE(PROGRESS, 1))
+ set_current_file_index(file, ndx);
+ stats.xferred_files++;
+ stats.total_transferred_size += F_LENGTH(file);
+
+ cleanup_got_literal = 0;
+
+ if (daemon_filter_list.head
+ && check_filter(&daemon_filter_list, FLOG, fname, 0) < 0) {
+ rprintf(FERROR, "attempt to hack rsync failed.\n");
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ if (read_batch) {
+ int wanted = redoing
+ ? we_want_redo(ndx)
+ : gen_wants_ndx(ndx, cur_flist->flist_num);
+ if (!wanted) {
+ rprintf(FINFO,
+ "(Skipping batched update for%s \"%s\")\n",
+ redoing ? " resend of" : "",
+ fname);
+ discard_receive_data(f_in, F_LENGTH(file));
+ file->flags |= FLAG_FILE_SENT;
+ continue;
+ }
+ }
+
+ if (!log_before_transfer)
+ remember_initial_stats();
+
+ if (!do_xfers) { /* log the transfer */
+ log_item(FCLIENT, file, iflags, NULL);
+ if (read_batch)
+ discard_receive_data(f_in, F_LENGTH(file));
+ continue;
+ }
+ if (write_batch < 0) {
+ log_item(FCLIENT, file, iflags, NULL);
+ if (!am_server)
+ discard_receive_data(f_in, F_LENGTH(file));
+ if (inc_recurse)
+ send_msg_int(MSG_SUCCESS, ndx);
+ continue;
+ }
+
+ partialptr = partial_dir ? partial_dir_fname(fname) : fname;
+
+ if (protocol_version >= 29) {
+ switch (fnamecmp_type) {
+ case FNAMECMP_FNAME:
+ fnamecmp = fname;
+ break;
+ case FNAMECMP_PARTIAL_DIR:
+ fnamecmp = partialptr;
+ break;
+ case FNAMECMP_BACKUP:
+ fnamecmp = get_backup_name(fname);
+ break;
+ case FNAMECMP_FUZZY:
+ if (file->dirname) {
+ pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, file->dirname, xname);
+ fnamecmp = fnamecmpbuf;
+ } else
+ fnamecmp = xname;
+ break;
+ default:
+ if (fnamecmp_type > FNAMECMP_FUZZY && fnamecmp_type-FNAMECMP_FUZZY <= basis_dir_cnt) {
+ fnamecmp_type -= FNAMECMP_FUZZY + 1;
+ if (file->dirname) {
+ stringjoin(fnamecmpbuf, sizeof fnamecmpbuf,
+ basis_dir[fnamecmp_type], "/", file->dirname, "/", xname, NULL);
+ } else
+ pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[fnamecmp_type], xname);
+ } else if (fnamecmp_type >= basis_dir_cnt) {
+ rprintf(FERROR,
+ "invalid basis_dir index: %d.\n",
+ fnamecmp_type);
+ exit_cleanup(RERR_PROTOCOL);
+ } else
+ pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[fnamecmp_type], fname);
+ fnamecmp = fnamecmpbuf;
+ break;
+ }
+ if (!fnamecmp || (daemon_filter_list.head
+ && check_filter(&daemon_filter_list, FLOG, fname, 0) < 0)) {
+ fnamecmp = fname;
+ fnamecmp_type = FNAMECMP_FNAME;
+ }
+ } else {
+ /* Reminder: --inplace && --partial-dir are never
+ * enabled at the same time. */
+ if (inplace && make_backups > 0) {
+ if (!(fnamecmp = get_backup_name(fname)))
+ fnamecmp = fname;
+ else
+ fnamecmp_type = FNAMECMP_BACKUP;
+ } else if (partial_dir && partialptr)
+ fnamecmp = partialptr;
+ else
+ fnamecmp = fname;
+ }
+
+ /* open the file */
+ fd1 = do_open(fnamecmp, O_RDONLY, 0);
+
+ if (fd1 == -1 && protocol_version < 29) {
+ if (fnamecmp != fname) {
+ fnamecmp = fname;
+ fd1 = do_open(fnamecmp, O_RDONLY, 0);
+ }
+
+ if (fd1 == -1 && basis_dir[0]) {
+ /* pre-29 allowed only one alternate basis */
+ pathjoin(fnamecmpbuf, sizeof fnamecmpbuf,
+ basis_dir[0], fname);
+ fnamecmp = fnamecmpbuf;
+ fd1 = do_open(fnamecmp, O_RDONLY, 0);
+ }
+ }
+
+ updating_basis_or_equiv = inplace
+ && (fnamecmp == fname || fnamecmp_type == FNAMECMP_BACKUP);
+
+ if (fd1 == -1) {
+ st.st_mode = 0;
+ st.st_size = 0;
+ } else if (do_fstat(fd1,&st) != 0) {
+ rsyserr(FERROR_XFER, errno, "fstat %s failed",
+ full_fname(fnamecmp));
+ discard_receive_data(f_in, F_LENGTH(file));
+ close(fd1);
+ if (inc_recurse)
+ send_msg_int(MSG_NO_SEND, ndx);
+ continue;
+ }
+
+ if (fd1 != -1 && S_ISDIR(st.st_mode) && fnamecmp == fname) {
+ /* this special handling for directories
+ * wouldn't be necessary if robust_rename()
+ * and the underlying robust_unlink could cope
+ * with directories
+ */
+ rprintf(FERROR_XFER, "recv_files: %s is a directory\n",
+ full_fname(fnamecmp));
+ discard_receive_data(f_in, F_LENGTH(file));
+ close(fd1);
+ if (inc_recurse)
+ send_msg_int(MSG_NO_SEND, ndx);
+ continue;
+ }
+
+ if (fd1 != -1 && !S_ISREG(st.st_mode)) {
+ close(fd1);
+ fd1 = -1;
+ }
+
+ /* If we're not preserving permissions, change the file-list's
+ * mode based on the local permissions and some heuristics. */
+ if (!preserve_perms) {
+ int exists = fd1 != -1;
+#ifdef SUPPORT_ACLS
+ const char *dn = file->dirname ? file->dirname : ".";
+ if (parent_dirname != dn
+ && strcmp(parent_dirname, dn) != 0) {
+ dflt_perms = default_perms_for_dir(dn);
+ parent_dirname = dn;
+ }
+#endif
+ file->mode = dest_mode(file->mode, st.st_mode,
+ dflt_perms, exists);
+ }
+
+ /* We now check to see if we are writing the file "inplace" */
+ if (inplace) {
+ fd2 = do_open(fname, O_WRONLY|O_CREAT, 0600);
+ if (fd2 == -1) {
+ rsyserr(FERROR_XFER, errno, "open %s failed",
+ full_fname(fname));
+ } else if (updating_basis_or_equiv)
+ cleanup_set(NULL, NULL, file, fd1, fd2);
+ } else {
+ fd2 = open_tmpfile(fnametmp, fname, file);
+ if (fd2 != -1)
+ cleanup_set(fnametmp, partialptr, file, fd1, fd2);
+ }
+
+ if (fd2 == -1) {
+ discard_receive_data(f_in, F_LENGTH(file));
+ if (fd1 != -1)
+ close(fd1);
+ if (inc_recurse)
+ send_msg_int(MSG_NO_SEND, ndx);
+ continue;
+ }
+
+ /* log the transfer */
+ if (log_before_transfer)
+ log_item(FCLIENT, file, iflags, NULL);
+ else if (!am_server && INFO_GTE(NAME, 1) && INFO_EQ(PROGRESS, 1))
+ rprintf(FINFO, "%s\n", fname);
+
+ /* recv file data */
+ recv_ok = receive_data(f_in, fnamecmp, fd1, st.st_size,
+ fname, fd2, F_LENGTH(file));
+
+ log_item(log_code, file, iflags, NULL);
+
+ if (fd1 != -1)
+ close(fd1);
+ if (close(fd2) < 0) {
+ rsyserr(FERROR, errno, "close failed on %s",
+ full_fname(fnametmp));
+ exit_cleanup(RERR_FILEIO);
+ }
+
+ if ((recv_ok && (!delay_updates || !partialptr)) || inplace) {
+ if (partialptr == fname)
+ partialptr = NULL;
+ if (!finish_transfer(fname, fnametmp, fnamecmp,
+ partialptr, file, recv_ok, 1))
+ recv_ok = -1;
+ else if (fnamecmp == partialptr) {
+ do_unlink(partialptr);
+ handle_partial_dir(partialptr, PDIR_DELETE);
+ }
+ } else if (keep_partial && partialptr) {
+ if (!handle_partial_dir(partialptr, PDIR_CREATE)) {
+ rprintf(FERROR,
+ "Unable to create partial-dir for %s -- discarding %s.\n",
+ local_name ? local_name : f_name(file, NULL),
+ recv_ok ? "completed file" : "partial file");
+ do_unlink(fnametmp);
+ recv_ok = -1;
+ } else if (!finish_transfer(partialptr, fnametmp, fnamecmp, NULL,
+ file, recv_ok, !partial_dir))
+ recv_ok = -1;
+ else if (delay_updates && recv_ok) {
+ bitbag_set_bit(delayed_bits, ndx);
+ recv_ok = 2;
+ } else
+ partialptr = NULL;
+ } else
+ do_unlink(fnametmp);
+
+ cleanup_disable();
+
+ if (read_batch)
+ file->flags |= FLAG_FILE_SENT;
+
+ switch (recv_ok) {
+ case 2:
+ break;
+ case 1:
+ if (remove_source_files || inc_recurse
+ || (preserve_hard_links && F_IS_HLINKED(file)))
+ send_msg_int(MSG_SUCCESS, ndx);
+ break;
+ case 0: {
+ enum logcode msgtype = redoing ? FERROR_XFER : FWARNING;
+ if (msgtype == FERROR_XFER || INFO_GTE(NAME, 1)) {
+ char *errstr, *redostr, *keptstr;
+ if (!(keep_partial && partialptr) && !inplace)
+ keptstr = "discarded";
+ else if (partial_dir)
+ keptstr = "put into partial-dir";
+ else
+ keptstr = "retained";
+ if (msgtype == FERROR_XFER) {
+ errstr = "ERROR";
+ redostr = "";
+ } else {
+ errstr = "WARNING";
+ redostr = read_batch ? " (may try again)"
+ : " (will try again)";
+ }
+ rprintf(msgtype,
+ "%s: %s failed verification -- update %s%s.\n",
+ errstr, local_name ? f_name(file, NULL) : fname,
+ keptstr, redostr);
+ }
+ if (!redoing) {
+ if (read_batch)
+ flist_ndx_push(&batch_redo_list, ndx);
+ send_msg_int(MSG_REDO, ndx);
+ file->flags |= FLAG_FILE_SENT;
+ } else if (inc_recurse)
+ send_msg_int(MSG_NO_SEND, ndx);
+ break;
+ }
+ case -1:
+ if (inc_recurse)
+ send_msg_int(MSG_NO_SEND, ndx);
+ break;
+ }
+ }
+ if (make_backups < 0)
+ make_backups = -make_backups;
+
+ if (phase == 2 && delay_updates) /* for protocol_version < 29 */
+ handle_delayed_updates(local_name);
+
+ if (DEBUG_GTE(RECV, 1))
+ rprintf(FINFO,"recv_files finished\n");
+
+ return 0;
+}
diff --git a/rsync/rounding.c b/rsync/rounding.c
new file mode 100644
index 0000000..ea9604f
--- /dev/null
+++ b/rsync/rounding.c
@@ -0,0 +1,38 @@
+/*
+ * A pre-compilation helper program to aid in the creation of rounding.h.
+ *
+ * Copyright (C) 2007-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+#define ARRAY_LEN (EXTRA_ROUNDING+1)
+#define SIZEOF(x) ((long int)sizeof (x))
+
+struct test {
+ union file_extras extras[ARRAY_LEN];
+ struct file_struct file;
+};
+
+#define ACTUAL_SIZE SIZEOF(struct test)
+#define EXPECTED_SIZE (SIZEOF(union file_extras) * ARRAY_LEN + SIZEOF(struct file_struct))
+
+ int main(UNUSED(int argc), UNUSED(char *argv[]))
+{
+ static int test_array[1 - 2 * (ACTUAL_SIZE != EXPECTED_SIZE)];
+ test_array[0] = 0;
+ return 0;
+}
diff --git a/rsync/rsync-3.1.1/.gitignore b/rsync/rsync-3.1.1/.gitignore
new file mode 100644
index 0000000..948d3f7
--- /dev/null
+++ b/rsync/rsync-3.1.1/.gitignore
@@ -0,0 +1,46 @@
+*.[oa]
+*~
+dummy
+ID
+Makefile
+Makefile.old
+configure.sh
+configure.sh.old
+config.cache
+config.h
+config.h.in
+config.h.in.old
+config.log
+config.status
+/proto.h
+/proto.h-tstamp
+/rsync.1
+/rsyncd.conf.5
+/autom4te*.cache
+/confdefs.h
+/conftest*
+/dox
+/getgroups
+/gmon.out
+/rsync
+/rsync-ssl
+/stunnel-rsync
+/stunnel-rsyncd.conf
+/shconfig
+/testdir
+/tests-dont-exist
+/testtmp
+/tls
+/testrun
+/trimslash
+/t_unsafe
+/wildtest
+/getfsdev
+/rounding.h
+/doc/rsync.pdf
+/doc/rsync.ps
+/support/savetransfer
+/testsuite/chown-fake.test
+/testsuite/devices-fake.test
+/testsuite/xattrs-hlink.test
+/patches
diff --git a/rsync/rsync-ssl.in b/rsync/rsync-ssl.in
new file mode 100755
index 0000000..da58d6a
--- /dev/null
+++ b/rsync/rsync-ssl.in
@@ -0,0 +1,12 @@
+#!/bin/bash
+# This script supports using stunnel to secure an rsync daemon connection.
+# Note that this requires at least version 4.x of stunnel.
+case "$@" in
+*rsync://*) ;;
+*::*) ;;
+*)
+ echo "You must use rsync-ssl with a daemon-style hostname." 0>&1
+ exit 1
+ ;;
+esac
+exec @bindir@/rsync --rsh=@bindir@/stunnel-rsync "${@}"
diff --git a/rsync/rsync.c b/rsync/rsync.c
new file mode 100644
index 0000000..c498c44
--- /dev/null
+++ b/rsync/rsync.c
@@ -0,0 +1,747 @@
+/*
+ * Routines common to more than one of the rsync processes.
+ *
+ * Copyright (C) 1996 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2003-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "ifuncs.h"
+#if defined HAVE_LIBCHARSET_H && defined HAVE_LOCALE_CHARSET
+#include
+#elif defined HAVE_LANGINFO_H && defined HAVE_NL_LANGINFO
+#include
+#endif
+
+extern int dry_run;
+extern int preserve_acls;
+extern int preserve_xattrs;
+extern int preserve_perms;
+extern int preserve_executability;
+extern int preserve_times;
+extern int am_root;
+extern int am_server;
+extern int am_daemon;
+extern int am_sender;
+extern int am_receiver;
+extern int am_generator;
+extern int am_starting_up;
+extern int allow_8bit_chars;
+extern int protocol_version;
+extern int got_kill_signal;
+extern int inc_recurse;
+extern int inplace;
+extern int flist_eof;
+extern int file_old_total;
+extern int keep_dirlinks;
+extern int make_backups;
+extern struct file_list *cur_flist, *first_flist, *dir_flist;
+extern struct chmod_mode_struct *daemon_chmod_modes;
+#ifdef ICONV_OPTION
+extern char *iconv_opt;
+#endif
+
+#ifdef ICONV_CONST
+iconv_t ic_chck = (iconv_t)-1;
+# ifdef ICONV_OPTION
+iconv_t ic_send = (iconv_t)-1, ic_recv = (iconv_t)-1;
+# endif
+
+static const char *default_charset(void)
+{
+# if defined HAVE_LIBCHARSET_H && defined HAVE_LOCALE_CHARSET
+ return locale_charset();
+# elif defined HAVE_LANGINFO_H && defined HAVE_NL_LANGINFO
+ return nl_langinfo(CODESET);
+# else
+ return ""; /* Works with (at the very least) gnu iconv... */
+# endif
+}
+
+void setup_iconv(void)
+{
+ const char *defset = default_charset();
+# ifdef ICONV_OPTION
+ const char *charset;
+ char *cp;
+# endif
+
+ if (!am_server && !allow_8bit_chars) {
+ /* It's OK if this fails... */
+ ic_chck = iconv_open(defset, defset);
+
+ if (DEBUG_GTE(ICONV, 2)) {
+ if (ic_chck == (iconv_t)-1) {
+ rprintf(FINFO,
+ "msg checking via isprint()"
+ " (iconv_open(\"%s\", \"%s\") errno: %d)\n",
+ defset, defset, errno);
+ } else {
+ rprintf(FINFO,
+ "msg checking charset: %s\n",
+ defset);
+ }
+ }
+ } else
+ ic_chck = (iconv_t)-1;
+
+# ifdef ICONV_OPTION
+ if (!iconv_opt)
+ return;
+
+ if ((cp = strchr(iconv_opt, ',')) != NULL) {
+ if (am_server) /* A local transfer needs this. */
+ iconv_opt = cp + 1;
+ else
+ *cp = '\0';
+ }
+
+ if (!*iconv_opt || (*iconv_opt == '.' && iconv_opt[1] == '\0'))
+ charset = defset;
+ else
+ charset = iconv_opt;
+
+ if ((ic_send = iconv_open(UTF8_CHARSET, charset)) == (iconv_t)-1) {
+ rprintf(FERROR, "iconv_open(\"%s\", \"%s\") failed\n",
+ UTF8_CHARSET, charset);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+
+ if ((ic_recv = iconv_open(charset, UTF8_CHARSET)) == (iconv_t)-1) {
+ rprintf(FERROR, "iconv_open(\"%s\", \"%s\") failed\n",
+ charset, UTF8_CHARSET);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+
+ if (DEBUG_GTE(ICONV, 1)) {
+ rprintf(FINFO, "[%s] charset: %s\n",
+ who_am_i(), *charset ? charset : "[LOCALE]");
+ }
+# endif
+}
+
+/* This function converts the chars in the "in" xbuf into characters in the
+ * "out" xbuf. The ".len" chars of the "in" xbuf is used starting from its
+ * ".pos". The ".size" of the "out" xbuf restricts how many characters can
+ * be stored, starting at its ".pos+.len" position. Note that the last byte
+ * of the "out" xbuf is not used, which reserves space for a trailing '\0'
+ * (though it is up to the caller to store a trailing '\0', as needed).
+ *
+ * We return a 0 on success or a -1 on error. An error also sets errno to
+ * E2BIG, EILSEQ, or EINVAL (see below); otherwise errno will be set to 0.
+ * The "in" xbuf is altered to update ".pos" and ".len". The "out" xbuf has
+ * data appended, and its ".len" incremented (see below for a ".size" note).
+ *
+ * If ICB_CIRCULAR_OUT is set in "flags", the chars going into the "out" xbuf
+ * can wrap around to the start, and the xbuf may have its ".size" reduced
+ * (presumably by 1 byte) if the iconv code doesn't have space to store a
+ * multi-byte character at the physical end of the ".buf" (though no reducing
+ * happens if ".pos" is <= 1, since there is no room to wrap around).
+ *
+ * If ICB_EXPAND_OUT is set in "flags", the "out" xbuf will be allocated if
+ * empty, and (as long as ICB_CIRCULAR_OUT is not set) expanded if too small.
+ * This prevents the return of E2BIG (except for a circular xbuf).
+ *
+ * If ICB_INCLUDE_BAD is set in "flags", any badly-encoded chars are included
+ * verbatim in the "out" xbuf, so EILSEQ will not be returned.
+ *
+ * If ICB_INCLUDE_INCOMPLETE is set in "flags", any incomplete multi-byte
+ * chars are included, which ensures that EINVAL is not returned.
+ *
+ * If ICB_INIT is set, the iconv() conversion state is initialized prior to
+ * processing the characters. */
+int iconvbufs(iconv_t ic, xbuf *in, xbuf *out, int flags)
+{
+ ICONV_CONST char *ibuf;
+ size_t icnt, ocnt, opos;
+ char *obuf;
+
+ if (!out->size && flags & ICB_EXPAND_OUT) {
+ size_t siz = ROUND_UP_1024(in->len * 2);
+ alloc_xbuf(out, siz);
+ } else if (out->len+1 >= out->size) {
+ /* There is no room to even start storing data. */
+ if (!(flags & ICB_EXPAND_OUT) || flags & ICB_CIRCULAR_OUT) {
+ errno = E2BIG;
+ return -1;
+ }
+ realloc_xbuf(out, out->size + ROUND_UP_1024(in->len * 2));
+ }
+
+ if (flags & ICB_INIT)
+ iconv(ic, NULL, 0, NULL, 0);
+
+ ibuf = in->buf + in->pos;
+ icnt = in->len;
+
+ opos = out->pos + out->len;
+ if (flags & ICB_CIRCULAR_OUT) {
+ if (opos >= out->size) {
+ opos -= out->size;
+ /* We know that out->pos is not 0 due to the "no room" check
+ * above, so this can't go "negative". */
+ ocnt = out->pos - opos - 1;
+ } else {
+ /* Allow the use of all bytes to the physical end of the buffer
+ * unless pos is 0, in which case we reserve our trailing '\0'. */
+ ocnt = out->size - opos - (out->pos ? 0 : 1);
+ }
+ } else
+ ocnt = out->size - opos - 1;
+ obuf = out->buf + opos;
+
+ while (icnt) {
+ while (iconv(ic, &ibuf, &icnt, &obuf, &ocnt) == (size_t)-1) {
+ if (errno == EINTR)
+ continue;
+ if (errno == EINVAL) {
+ if (!(flags & ICB_INCLUDE_INCOMPLETE))
+ goto finish;
+ if (!ocnt)
+ goto e2big;
+ } else if (errno == EILSEQ) {
+ if (!(flags & ICB_INCLUDE_BAD))
+ goto finish;
+ if (!ocnt)
+ goto e2big;
+ } else if (errno == E2BIG) {
+ size_t siz;
+ e2big:
+ opos = obuf - out->buf;
+ if (flags & ICB_CIRCULAR_OUT && out->pos > 1 && opos > out->pos) {
+ /* We are in a divided circular buffer at the physical
+ * end with room to wrap to the start. If iconv() refused
+ * to use one or more trailing bytes in the buffer, we
+ * set the size to ignore the unused bytes. */
+ if (opos < out->size)
+ reduce_iobuf_size(out, opos);
+ obuf = out->buf;
+ ocnt = out->pos - 1;
+ continue;
+ }
+ if (!(flags & ICB_EXPAND_OUT) || flags & ICB_CIRCULAR_OUT) {
+ errno = E2BIG;
+ goto finish;
+ }
+ siz = ROUND_UP_1024(in->len * 2);
+ realloc_xbuf(out, out->size + siz);
+ obuf = out->buf + opos;
+ ocnt += siz;
+ continue;
+ } else {
+ rsyserr(FERROR, errno, "unexpected error from iconv()");
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+ *obuf++ = *ibuf++;
+ ocnt--, icnt--;
+ if (!icnt)
+ break;
+ }
+ }
+
+ errno = 0;
+
+ finish:
+ opos = obuf - out->buf;
+ if (flags & ICB_CIRCULAR_OUT && opos < out->pos)
+ opos += out->size;
+ out->len = opos - out->pos;
+
+ in->len = icnt;
+ in->pos = ibuf - in->buf;
+
+ return errno ? -1 : 0;
+}
+#endif
+
+void send_protected_args(int fd, char *args[])
+{
+ int i;
+#ifdef ICONV_OPTION
+ int convert = ic_send != (iconv_t)-1;
+ xbuf outbuf, inbuf;
+
+ if (convert)
+ alloc_xbuf(&outbuf, 1024);
+#endif
+
+ for (i = 0; args[i]; i++) {} /* find first NULL */
+ args[i] = "rsync"; /* set a new arg0 */
+ if (DEBUG_GTE(CMD, 1))
+ print_child_argv("protected args:", args + i + 1);
+ do {
+ if (!args[i][0])
+ write_buf(fd, ".", 2);
+#ifdef ICONV_OPTION
+ else if (convert) {
+ INIT_XBUF_STRLEN(inbuf, args[i]);
+ iconvbufs(ic_send, &inbuf, &outbuf,
+ ICB_EXPAND_OUT | ICB_INCLUDE_BAD | ICB_INCLUDE_INCOMPLETE | ICB_INIT);
+ outbuf.buf[outbuf.len] = '\0';
+ write_buf(fd, outbuf.buf, outbuf.len + 1);
+ outbuf.len = 0;
+ }
+#endif
+ else
+ write_buf(fd, args[i], strlen(args[i]) + 1);
+ } while (args[++i]);
+ write_byte(fd, 0);
+
+#ifdef ICONV_OPTION
+ if (convert)
+ free(outbuf.buf);
+#endif
+}
+
+int read_ndx_and_attrs(int f_in, int f_out, int *iflag_ptr, uchar *type_ptr,
+ char *buf, int *len_ptr)
+{
+ int len, iflags = 0;
+ struct file_list *flist;
+ uchar fnamecmp_type = FNAMECMP_FNAME;
+ int ndx;
+
+ read_loop:
+ while (1) {
+ ndx = read_ndx(f_in);
+
+ if (ndx >= 0)
+ break;
+ if (ndx == NDX_DONE)
+ return ndx;
+ if (ndx == NDX_DEL_STATS) {
+ read_del_stats(f_in);
+ if (am_sender && am_server)
+ write_del_stats(f_out);
+ continue;
+ }
+ if (!inc_recurse || am_sender) {
+ int last;
+ if (first_flist)
+ last = first_flist->prev->ndx_start + first_flist->prev->used - 1;
+ else
+ last = -1;
+ rprintf(FERROR,
+ "Invalid file index: %d (%d - %d) [%s]\n",
+ ndx, NDX_DONE, last, who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ if (ndx == NDX_FLIST_EOF) {
+ flist_eof = 1;
+ if (DEBUG_GTE(FLIST, 3))
+ rprintf(FINFO, "[%s] flist_eof=1\n", who_am_i());
+ write_int(f_out, NDX_FLIST_EOF);
+ continue;
+ }
+ ndx = NDX_FLIST_OFFSET - ndx;
+ if (ndx < 0 || ndx >= dir_flist->used) {
+ ndx = NDX_FLIST_OFFSET - ndx;
+ rprintf(FERROR,
+ "Invalid dir index: %d (%d - %d) [%s]\n",
+ ndx, NDX_FLIST_OFFSET,
+ NDX_FLIST_OFFSET - dir_flist->used + 1,
+ who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ if (DEBUG_GTE(FLIST, 2)) {
+ rprintf(FINFO, "[%s] receiving flist for dir %d\n",
+ who_am_i(), ndx);
+ }
+ /* Send all the data we read for this flist to the generator. */
+ start_flist_forward(ndx);
+ flist = recv_file_list(f_in);
+ flist->parent_ndx = ndx;
+ stop_flist_forward();
+ }
+
+ iflags = protocol_version >= 29 ? read_shortint(f_in)
+ : ITEM_TRANSFER | ITEM_MISSING_DATA;
+
+ /* Support the protocol-29 keep-alive style. */
+ if (protocol_version < 30 && ndx == cur_flist->used && iflags == ITEM_IS_NEW) {
+ if (am_sender)
+ maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH);
+ goto read_loop;
+ }
+
+ flist = flist_for_ndx(ndx, "read_ndx_and_attrs");
+ if (flist != cur_flist) {
+ cur_flist = flist;
+ if (am_sender) {
+ file_old_total = cur_flist->used;
+ for (flist = first_flist; flist != cur_flist; flist = flist->next)
+ file_old_total += flist->used;
+ }
+ }
+
+ if (iflags & ITEM_BASIS_TYPE_FOLLOWS)
+ fnamecmp_type = read_byte(f_in);
+ *type_ptr = fnamecmp_type;
+
+ if (iflags & ITEM_XNAME_FOLLOWS) {
+ if ((len = read_vstring(f_in, buf, MAXPATHLEN)) < 0)
+ exit_cleanup(RERR_PROTOCOL);
+ } else {
+ *buf = '\0';
+ len = -1;
+ }
+ *len_ptr = len;
+
+ if (iflags & ITEM_TRANSFER) {
+ int i = ndx - cur_flist->ndx_start;
+ if (i < 0 || !S_ISREG(cur_flist->files[i]->mode)) {
+ rprintf(FERROR,
+ "received request to transfer non-regular file: %d [%s]\n",
+ ndx, who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ }
+
+ *iflag_ptr = iflags;
+ return ndx;
+}
+
+/*
+ free a sums struct
+ */
+void free_sums(struct sum_struct *s)
+{
+ if (s->sums) free(s->sums);
+ free(s);
+}
+
+/* This is only called when we aren't preserving permissions. Figure out what
+ * the permissions should be and return them merged back into the mode. */
+mode_t dest_mode(mode_t flist_mode, mode_t stat_mode, int dflt_perms,
+ int exists)
+{
+ int new_mode;
+ /* If the file already exists, we'll return the local permissions,
+ * possibly tweaked by the --executability option. */
+ if (exists) {
+ new_mode = (flist_mode & ~CHMOD_BITS) | (stat_mode & CHMOD_BITS);
+ if (preserve_executability && S_ISREG(flist_mode)) {
+ /* If the source file is executable, grant execute
+ * rights to everyone who can read, but ONLY if the
+ * file isn't already executable. */
+ if (!(flist_mode & 0111))
+ new_mode &= ~0111;
+ else if (!(stat_mode & 0111))
+ new_mode |= (new_mode & 0444) >> 2;
+ }
+ } else {
+ /* Apply destination default permissions and turn
+ * off special permissions. */
+ new_mode = flist_mode & (~CHMOD_BITS | dflt_perms);
+ }
+ return new_mode;
+}
+
+int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
+ const char *fnamecmp, int flags)
+{
+ int updated = 0;
+ stat_x sx2;
+ int change_uid, change_gid;
+ mode_t new_mode = file->mode;
+ int inherit;
+
+ if (!sxp) {
+ if (dry_run)
+ return 1;
+ if (link_stat(fname, &sx2.st, 0) < 0) {
+ rsyserr(FERROR_XFER, errno, "stat %s failed",
+ full_fname(fname));
+ return 0;
+ }
+ init_stat_x(&sx2);
+ sxp = &sx2;
+ inherit = !preserve_perms;
+ } else
+ inherit = !preserve_perms && file->flags & FLAG_DIR_CREATED;
+
+ if (inherit && S_ISDIR(new_mode) && sxp->st.st_mode & S_ISGID) {
+ /* We just created this directory and its setgid
+ * bit is on, so make sure it stays on. */
+ new_mode |= S_ISGID;
+ }
+
+ if (daemon_chmod_modes && !S_ISLNK(new_mode))
+ new_mode = tweak_mode(new_mode, daemon_chmod_modes);
+
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && !S_ISLNK(file->mode) && !ACL_READY(*sxp))
+ get_acl(fname, sxp);
+#endif
+
+ change_uid = am_root && uid_ndx && sxp->st.st_uid != (uid_t)F_OWNER(file);
+ change_gid = gid_ndx && !(file->flags & FLAG_SKIP_GROUP)
+ && sxp->st.st_gid != (gid_t)F_GROUP(file);
+#ifndef CAN_CHOWN_SYMLINK
+ if (S_ISLNK(sxp->st.st_mode)) {
+ ;
+ } else
+#endif
+ if (change_uid || change_gid) {
+ if (DEBUG_GTE(OWN, 1)) {
+ if (change_uid) {
+ rprintf(FINFO,
+ "set uid of %s from %u to %u\n",
+ fname, (unsigned)sxp->st.st_uid, F_OWNER(file));
+ }
+ if (change_gid) {
+ rprintf(FINFO,
+ "set gid of %s from %u to %u\n",
+ fname, (unsigned)sxp->st.st_gid, F_GROUP(file));
+ }
+ }
+ if (am_root >= 0) {
+ uid_t uid = change_uid ? (uid_t)F_OWNER(file) : sxp->st.st_uid;
+ gid_t gid = change_gid ? (gid_t)F_GROUP(file) : sxp->st.st_gid;
+ if (do_lchown(fname, uid, gid) != 0) {
+ /* We shouldn't have attempted to change uid
+ * or gid unless have the privilege. */
+ rsyserr(FERROR_XFER, errno, "%s %s failed",
+ change_uid ? "chown" : "chgrp",
+ full_fname(fname));
+ goto cleanup;
+ }
+ if (uid == (uid_t)-1 && sxp->st.st_uid != (uid_t)-1)
+ rprintf(FERROR_XFER, "uid 4294967295 (-1) is impossible to set on %s\n", full_fname(fname));
+ if (gid == (gid_t)-1 && sxp->st.st_gid != (gid_t)-1)
+ rprintf(FERROR_XFER, "gid 4294967295 (-1) is impossible to set on %s\n", full_fname(fname));
+ /* A lchown had been done, so we need to re-stat if
+ * the destination had the setuid or setgid bits set
+ * (due to the side effect of the chown call). */
+ if (sxp->st.st_mode & (S_ISUID | S_ISGID)) {
+ link_stat(fname, &sxp->st,
+ keep_dirlinks && S_ISDIR(sxp->st.st_mode));
+ }
+ }
+ updated = 1;
+ }
+
+#ifdef SUPPORT_XATTRS
+ if (am_root < 0)
+ set_stat_xattr(fname, file, new_mode);
+ if (preserve_xattrs && fnamecmp)
+ set_xattr(fname, file, fnamecmp, sxp);
+#endif
+
+ if (!preserve_times
+ || (!(preserve_times & PRESERVE_DIR_TIMES) && S_ISDIR(sxp->st.st_mode))
+ || (!(preserve_times & PRESERVE_LINK_TIMES) && S_ISLNK(sxp->st.st_mode)))
+ flags |= ATTRS_SKIP_MTIME;
+ if (!(flags & ATTRS_SKIP_MTIME)
+ && cmp_time(sxp->st.st_mtime, file->modtime) != 0) {
+ int ret = set_modtime(fname, file->modtime, F_MOD_NSEC(file), sxp->st.st_mode);
+ if (ret < 0) {
+ rsyserr(FERROR_XFER, errno, "failed to set times on %s",
+ full_fname(fname));
+ goto cleanup;
+ }
+ if (ret == 0) /* ret == 1 if symlink could not be set */
+ updated = 1;
+ else
+ file->flags |= FLAG_TIME_FAILED;
+ }
+
+#ifdef SUPPORT_ACLS
+ /* It's OK to call set_acl() now, even for a dir, as the generator
+ * will enable owner-writability using chmod, if necessary.
+ *
+ * If set_acl() changes permission bits in the process of setting
+ * an access ACL, it changes sxp->st.st_mode so we know whether we
+ * need to chmod(). */
+ if (preserve_acls && !S_ISLNK(new_mode)) {
+ if (set_acl(fname, file, sxp, new_mode) > 0)
+ updated = 1;
+ }
+#endif
+
+#ifdef HAVE_CHMOD
+ if (!BITS_EQUAL(sxp->st.st_mode, new_mode, CHMOD_BITS)) {
+ int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode);
+ if (ret < 0) {
+ rsyserr(FERROR_XFER, errno,
+ "failed to set permissions on %s",
+ full_fname(fname));
+ goto cleanup;
+ }
+ if (ret == 0) /* ret == 1 if symlink could not be set */
+ updated = 1;
+ }
+#endif
+
+ if (INFO_GTE(NAME, 2) && flags & ATTRS_REPORT) {
+ if (updated)
+ rprintf(FCLIENT, "%s\n", fname);
+ else
+ rprintf(FCLIENT, "%s is uptodate\n", fname);
+ }
+ cleanup:
+ if (sxp == &sx2)
+ free_stat_x(&sx2);
+ return updated;
+}
+
+/* This is only called for SIGINT, SIGHUP, and SIGTERM. */
+RETSIGTYPE sig_int(int sig_num)
+{
+ /* KLUGE: if the user hits Ctrl-C while ssh is prompting
+ * for a password, then our cleanup's sending of a SIGUSR1
+ * signal to all our children may kill ssh before it has a
+ * chance to restore the tty settings (i.e. turn echo back
+ * on). By sleeping for a short time, ssh gets a bigger
+ * chance to do the right thing. If child processes are
+ * not ssh waiting for a password, then this tiny delay
+ * shouldn't hurt anything. */
+ msleep(400);
+
+ /* If we're an rsync daemon listener (not a daemon server),
+ * we'll exit with status 0 if we received SIGTERM. */
+ if (am_daemon && !am_server && sig_num == SIGTERM)
+ exit_cleanup(0);
+
+ /* If the signal arrived on the server side (or for the receiver
+ * process on the client), we want to try to do a controlled shutdown
+ * that lets the client side (generator process) know what happened.
+ * To do this, we set a flag and let the normal process handle the
+ * shutdown. We only attempt this if multiplexed IO is in effect and
+ * we didn't already set the flag. */
+ if (!got_kill_signal && (am_server || am_receiver)) {
+ got_kill_signal = sig_num;
+ return;
+ }
+
+ exit_cleanup(RERR_SIGNAL);
+}
+
+/* Finish off a file transfer: renaming the file and setting the file's
+ * attributes (e.g. permissions, ownership, etc.). If the robust_rename()
+ * call is forced to copy the temp file and partialptr is both non-NULL and
+ * not an absolute path, we stage the file into the partial-dir and then
+ * rename it into place. This returns 1 on succcess or 0 on failure. */
+int finish_transfer(const char *fname, const char *fnametmp,
+ const char *fnamecmp, const char *partialptr,
+ struct file_struct *file, int ok_to_set_time,
+ int overwriting_basis)
+{
+ int ret;
+ const char *temp_copy_name = partialptr && *partialptr != '/' ? partialptr : NULL;
+
+ if (inplace) {
+ if (DEBUG_GTE(RECV, 1))
+ rprintf(FINFO, "finishing %s\n", fname);
+ fnametmp = fname;
+ goto do_set_file_attrs;
+ }
+
+ if (make_backups > 0 && overwriting_basis) {
+ int ok = make_backup(fname, False);
+ if (!ok)
+ return 1;
+ if (ok == 1 && fnamecmp == fname)
+ fnamecmp = get_backup_name(fname);
+ }
+
+ /* Change permissions before putting the file into place. */
+ set_file_attrs(fnametmp, file, NULL, fnamecmp,
+ ok_to_set_time ? 0 : ATTRS_SKIP_MTIME);
+
+ /* move tmp file over real file */
+ if (DEBUG_GTE(RECV, 1))
+ rprintf(FINFO, "renaming %s to %s\n", fnametmp, fname);
+ ret = robust_rename(fnametmp, fname, temp_copy_name, file->mode);
+ if (ret < 0) {
+ rsyserr(FERROR_XFER, errno, "%s %s -> \"%s\"",
+ ret == -2 ? "copy" : "rename",
+ full_fname(fnametmp), fname);
+ if (!partialptr || (ret == -2 && temp_copy_name)
+ || robust_rename(fnametmp, partialptr, NULL, file->mode) < 0)
+ do_unlink(fnametmp);
+ return 0;
+ }
+ if (ret == 0) {
+ /* The file was moved into place (not copied), so it's done. */
+ return 1;
+ }
+ /* The file was copied, so tweak the perms of the copied file. If it
+ * was copied to partialptr, move it into its final destination. */
+ fnametmp = temp_copy_name ? temp_copy_name : fname;
+
+ do_set_file_attrs:
+ set_file_attrs(fnametmp, file, NULL, fnamecmp,
+ ok_to_set_time ? 0 : ATTRS_SKIP_MTIME);
+
+ if (temp_copy_name) {
+ if (do_rename(fnametmp, fname) < 0) {
+ rsyserr(FERROR_XFER, errno, "rename %s -> \"%s\"",
+ full_fname(fnametmp), fname);
+ return 0;
+ }
+ handle_partial_dir(temp_copy_name, PDIR_DELETE);
+ }
+ return 1;
+}
+
+struct file_list *flist_for_ndx(int ndx, const char *fatal_error_loc)
+{
+ struct file_list *flist = cur_flist;
+
+ if (!flist && !(flist = first_flist))
+ goto not_found;
+
+ while (ndx < flist->ndx_start-1) {
+ if (flist == first_flist)
+ goto not_found;
+ flist = flist->prev;
+ }
+ while (ndx >= flist->ndx_start + flist->used) {
+ if (!(flist = flist->next))
+ goto not_found;
+ }
+ return flist;
+
+ not_found:
+ if (fatal_error_loc) {
+ int first, last;
+ if (first_flist) {
+ first = first_flist->ndx_start - 1;
+ last = first_flist->prev->ndx_start + first_flist->prev->used - 1;
+ } else {
+ first = 0;
+ last = -1;
+ }
+ rprintf(FERROR,
+ "File-list index %d not in %d - %d (%s) [%s]\n",
+ ndx, first, last, fatal_error_loc, who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ return NULL;
+}
+
+const char *who_am_i(void)
+{
+ if (am_starting_up)
+ return am_server ? "server" : "client";
+ return am_sender ? "sender"
+ : am_generator ? "generator"
+ : am_receiver ? "receiver"
+ : "Receiver"; /* pre-forked receiver */
+}
diff --git a/rsync/rsync.h b/rsync/rsync.h
new file mode 100644
index 0000000..4fef882
--- /dev/null
+++ b/rsync/rsync.h
@@ -0,0 +1,1294 @@
+/*
+ * Copyright (C) 1996, 2000 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2001, 2002 Martin Pool
+ * Copyright (C) 2003-2014 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#define False 0
+#define True 1
+
+#define BLOCK_SIZE 700
+#define RSYNC_RSH_ENV "RSYNC_RSH"
+#define RSYNC_RSH_IO_ENV "RSYNC_RSH_IO"
+
+#define RSYNC_NAME "rsync"
+/* RSYNCD_SYSCONF is now set in config.h */
+#define RSYNCD_USERCONF "rsyncd.conf"
+
+#define DEFAULT_LOCK_FILE "/var/run/rsyncd.lock"
+#define URL_PREFIX "rsync://"
+
+#define SYMLINK_PREFIX "/rsyncd-munged/" /* This MUST have a trailing slash! */
+#define SYMLINK_PREFIX_LEN ((int)sizeof SYMLINK_PREFIX - 1)
+
+#define BACKUP_SUFFIX "~"
+
+/* a non-zero CHAR_OFFSET makes the rolling sum stronger, but is
+ incompatible with older versions :-( */
+#define CHAR_OFFSET 0
+
+/* These flags are only used during the flist transfer. */
+
+#define XMIT_TOP_DIR (1<<0)
+#define XMIT_SAME_MODE (1<<1)
+#define XMIT_SAME_RDEV_pre28 (1<<2) /* protocols 20 - 27 */
+#define XMIT_EXTENDED_FLAGS (1<<2) /* protocols 28 - now */
+#define XMIT_SAME_UID (1<<3)
+#define XMIT_SAME_GID (1<<4)
+#define XMIT_SAME_NAME (1<<5)
+#define XMIT_LONG_NAME (1<<6)
+#define XMIT_SAME_TIME (1<<7)
+#define XMIT_SAME_RDEV_MAJOR (1<<8) /* protocols 28 - now (devices only) */
+#define XMIT_NO_CONTENT_DIR (1<<8) /* protocols 30 - now (dirs only) */
+#define XMIT_HLINKED (1<<9) /* protocols 28 - now */
+#define XMIT_SAME_DEV_pre30 (1<<10) /* protocols 28 - 29 */
+#define XMIT_USER_NAME_FOLLOWS (1<<10) /* protocols 30 - now */
+#define XMIT_RDEV_MINOR_8_pre30 (1<<11) /* protocols 28 - 29 */
+#define XMIT_GROUP_NAME_FOLLOWS (1<<11) /* protocols 30 - now */
+#define XMIT_HLINK_FIRST (1<<12) /* protocols 30 - now (HLINKED files only) */
+#define XMIT_IO_ERROR_ENDLIST (1<<12) /* protocols 31*- now (w/XMIT_EXTENDED_FLAGS) (also protocol 30 w/'f' compat flag) */
+#define XMIT_MOD_NSEC (1<<13) /* protocols 31 - now */
+
+/* These flags are used in the live flist data. */
+
+#define FLAG_TOP_DIR (1<<0) /* sender/receiver/generator */
+#define FLAG_OWNED_BY_US (1<<0) /* generator: set by make_file() for aux flists only */
+#define FLAG_FILE_SENT (1<<1) /* sender/receiver/generator */
+#define FLAG_DIR_CREATED (1<<1) /* generator */
+#define FLAG_CONTENT_DIR (1<<2) /* sender/receiver/generator */
+#define FLAG_MOUNT_DIR (1<<3) /* sender/generator (dirs only) */
+#define FLAG_SKIP_HLINK (1<<3) /* receiver/generator (w/FLAG_HLINKED) */
+#define FLAG_DUPLICATE (1<<4) /* sender */
+#define FLAG_MISSING_DIR (1<<4) /* generator */
+#define FLAG_HLINKED (1<<5) /* receiver/generator (checked on all types) */
+#define FLAG_HLINK_FIRST (1<<6) /* receiver/generator (w/FLAG_HLINKED) */
+#define FLAG_IMPLIED_DIR (1<<6) /* sender/receiver/generator (dirs only) */
+#define FLAG_HLINK_LAST (1<<7) /* receiver/generator */
+#define FLAG_HLINK_DONE (1<<8) /* receiver/generator (checked on all types) */
+#define FLAG_LENGTH64 (1<<9) /* sender/receiver/generator */
+#define FLAG_SKIP_GROUP (1<<10) /* receiver/generator */
+#define FLAG_TIME_FAILED (1<<11)/* generator */
+#define FLAG_MOD_NSEC (1<<12) /* sender/receiver/generator */
+
+/* These flags are passed to functions but not stored. */
+
+#define FLAG_DIVERT_DIRS (1<<16) /* sender, but must be unique */
+
+/* These flags are for get_dirlist(). */
+#define GDL_IGNORE_FILTER_RULES (1<<0)
+
+/* Some helper macros for matching bits. */
+#define BITS_SET(val,bits) (((val) & (bits)) == (bits))
+#define BITS_SETnUNSET(val,onbits,offbits) (((val) & ((onbits)|(offbits))) == (onbits))
+#define BITS_EQUAL(b1,b2,mask) (((unsigned)(b1) & (unsigned)(mask)) \
+ == ((unsigned)(b2) & (unsigned)(mask)))
+
+/* update this if you make incompatible changes */
+#define PROTOCOL_VERSION 31
+
+/* This is used when working on a new protocol version in CVS, and should
+ * be a new non-zero value for each CVS change that affects the protocol.
+ * It must ALWAYS be 0 when the protocol goes final (and NEVER before)! */
+#define SUBPROTOCOL_VERSION 0
+
+/* We refuse to interoperate with versions that are not in this range.
+ * Note that we assume we'll work with later versions: the onus is on
+ * people writing them to make sure that they don't send us anything
+ * we won't understand.
+ *
+ * Interoperation with old but supported protocol versions
+ * should cause a warning to be printed. At a future date
+ * the old protocol will become the minimum and
+ * compatibility code removed.
+ *
+ * There are two possible explanations for the limit at
+ * MAX_PROTOCOL_VERSION: either to allow new major-rev versions that
+ * do not interoperate with us, and (more likely) so that we can
+ * detect an attempt to connect rsync to a non-rsync server, which is
+ * unlikely to begin by sending a byte between MIN_PROTOCL_VERSION and
+ * MAX_PROTOCOL_VERSION. */
+
+#define MIN_PROTOCOL_VERSION 20
+#define OLD_PROTOCOL_VERSION 25
+#define MAX_PROTOCOL_VERSION 40
+
+#define MIN_FILECNT_LOOKAHEAD 1000
+#define MAX_FILECNT_LOOKAHEAD 10000
+
+#define RSYNC_PORT 873
+
+#define SPARSE_WRITE_SIZE (1024)
+#define WRITE_SIZE (32*1024)
+#define CHUNK_SIZE (32*1024)
+#define MAX_MAP_SIZE (256*1024)
+#define IO_BUFFER_SIZE (32*1024)
+#define MAX_BLOCK_SIZE ((int32)1 << 17)
+
+/* For compatibility with older rsyncs */
+#define OLD_MAX_BLOCK_SIZE ((int32)1 << 29)
+
+#define ROUND_UP_1024(siz) ((siz) & (1024-1) ? ((siz) | (1024-1)) + 1 : (siz))
+
+#define IOERR_GENERAL (1<<0) /* For backward compatibility, this must == 1 */
+#define IOERR_VANISHED (1<<1)
+#define IOERR_DEL_LIMIT (1<<2)
+
+#define MAX_ARGS 1000
+#define MAX_BASIS_DIRS 20
+#define MAX_SERVER_ARGS (MAX_BASIS_DIRS*2 + 100)
+
+#define MPLEX_BASE 7
+
+#define NO_FILTERS 0
+#define SERVER_FILTERS 1
+#define ALL_FILTERS 2
+
+#define XFLG_FATAL_ERRORS (1<<0)
+#define XFLG_OLD_PREFIXES (1<<1)
+#define XFLG_ANCHORED2ABS (1<<2) /* leading slash indicates absolute */
+#define XFLG_ABS_IF_SLASH (1<<3) /* leading or interior slash is absolute */
+#define XFLG_DIR2WILD3 (1<<4) /* dir/ match gets trailing *** added */
+
+#define ATTRS_REPORT (1<<0)
+#define ATTRS_SKIP_MTIME (1<<1)
+
+#define FULL_FLUSH 1
+#define NORMAL_FLUSH 0
+
+#define PDIR_CREATE 1
+#define PDIR_DELETE 0
+
+/* Note: 0x00 - 0x7F are used for basis_dir[] indexes! */
+#define FNAMECMP_BASIS_DIR_LOW 0x00 /* Must remain 0! */
+#define FNAMECMP_BASIS_DIR_HIGH 0x7F
+#define FNAMECMP_FNAME 0x80
+#define FNAMECMP_PARTIAL_DIR 0x81
+#define FNAMECMP_BACKUP 0x82
+#define FNAMECMP_FUZZY 0x83
+
+/* For use by the itemize_changes code */
+#define ITEM_REPORT_ATIME (1<<0)
+#define ITEM_REPORT_CHANGE (1<<1)
+#define ITEM_REPORT_SIZE (1<<2) /* regular files only */
+#define ITEM_REPORT_TIMEFAIL (1<<2) /* symlinks only */
+#define ITEM_REPORT_TIME (1<<3)
+#define ITEM_REPORT_PERMS (1<<4)
+#define ITEM_REPORT_OWNER (1<<5)
+#define ITEM_REPORT_GROUP (1<<6)
+#define ITEM_REPORT_ACL (1<<7)
+#define ITEM_REPORT_XATTR (1<<8)
+#define ITEM_BASIS_TYPE_FOLLOWS (1<<11)
+#define ITEM_XNAME_FOLLOWS (1<<12)
+#define ITEM_IS_NEW (1<<13)
+#define ITEM_LOCAL_CHANGE (1<<14)
+#define ITEM_TRANSFER (1<<15)
+/* These are outside the range of the transmitted flags. */
+#define ITEM_MISSING_DATA (1<<16) /* used by log_formatted() */
+#define ITEM_DELETED (1<<17) /* used by log_formatted() */
+#define ITEM_MATCHED (1<<18) /* used by itemize() */
+
+#define SIGNIFICANT_ITEM_FLAGS (~(\
+ ITEM_BASIS_TYPE_FOLLOWS | ITEM_XNAME_FOLLOWS | ITEM_LOCAL_CHANGE))
+
+#define CFN_KEEP_DOT_DIRS (1<<0)
+#define CFN_KEEP_TRAILING_SLASH (1<<1)
+#define CFN_DROP_TRAILING_DOT_DIR (1<<2)
+#define CFN_COLLAPSE_DOT_DOT_DIRS (1<<3)
+#define CFN_REFUSE_DOT_DOT_DIRS (1<<4)
+
+#define SP_DEFAULT 0
+#define SP_KEEP_DOT_DIRS (1<<0)
+
+#define CD_NORMAL 0
+#define CD_SKIP_CHDIR 1
+
+/* Log-message categories. FLOG only goes to the log file, not the client;
+ * FCLIENT is the opposite. */
+enum logcode {
+ FNONE=0, /* never sent */
+ FERROR_XFER=1, FINFO=2, /* sent over socket for any protocol */
+ FERROR=3, FWARNING=4, /* sent over socket for protocols >= 30 */
+ FERROR_SOCKET=5, FLOG=6, /* only sent via receiver -> generator pipe */
+ FERROR_UTF8=8, /* only sent via receiver -> generator pipe */
+ FCLIENT=7 /* never transmitted (e.g. server converts to FINFO) */
+};
+
+/* Messages types that are sent over the message channel. The logcode
+ * values must all be present here with identical numbers. */
+enum msgcode {
+ MSG_DATA=0, /* raw data on the multiplexed stream */
+ MSG_ERROR_XFER=FERROR_XFER, MSG_INFO=FINFO, /* remote logging */
+ MSG_ERROR=FERROR, MSG_WARNING=FWARNING, /* protocol-30 remote logging */
+ MSG_ERROR_SOCKET=FERROR_SOCKET, /* sibling logging */
+ MSG_ERROR_UTF8=FERROR_UTF8, /* sibling logging */
+ MSG_LOG=FLOG, MSG_CLIENT=FCLIENT, /* sibling logging */
+ MSG_REDO=9, /* reprocess indicated flist index */
+ MSG_STATS=10, /* message has stats data for generator */
+ MSG_IO_ERROR=22,/* the sending side had an I/O error */
+ MSG_IO_TIMEOUT=33,/* tell client about a daemon's timeout value */
+ MSG_NOOP=42, /* a do-nothing message (legacy protocol-30 only) */
+ MSG_ERROR_EXIT=86, /* synchronize an error exit (siblings and protocol >= 31) */
+ MSG_SUCCESS=100,/* successfully updated indicated flist index */
+ MSG_DELETED=101,/* successfully deleted a file on receiving side */
+ MSG_NO_SEND=102,/* sender failed to open a file we wanted */
+};
+
+#define NDX_DONE -1
+#define NDX_FLIST_EOF -2
+#define NDX_DEL_STATS -3
+#define NDX_FLIST_OFFSET -101
+
+/* For calling delete_item() and delete_dir_contents(). */
+#define DEL_NO_UID_WRITE (1<<0) /* file/dir has our uid w/o write perm */
+#define DEL_RECURSE (1<<1) /* if dir, delete all contents */
+#define DEL_DIR_IS_EMPTY (1<<2) /* internal delete_FUNCTIONS use only */
+#define DEL_FOR_FILE (1<<3) /* making room for a replacement file */
+#define DEL_FOR_DIR (1<<4) /* making room for a replacement dir */
+#define DEL_FOR_SYMLINK (1<<5) /* making room for a replacement symlink */
+#define DEL_FOR_DEVICE (1<<6) /* making room for a replacement device */
+#define DEL_FOR_SPECIAL (1<<7) /* making room for a replacement special */
+#define DEL_FOR_BACKUP (1<<8) /* the delete is for a backup operation */
+
+#define DEL_MAKE_ROOM (DEL_FOR_FILE|DEL_FOR_DIR|DEL_FOR_SYMLINK|DEL_FOR_DEVICE|DEL_FOR_SPECIAL)
+
+enum delret {
+ DR_SUCCESS = 0, DR_FAILURE, DR_AT_LIMIT, DR_NOT_EMPTY
+};
+
+/* Defines for make_path() */
+#define MKP_DROP_NAME (1<<0) /* drop trailing filename or trailing slash */
+#define MKP_SKIP_SLASH (1<<1) /* skip one or more leading slashes */
+
+/* Defines for maybe_send_keepalive() */
+#define MSK_ALLOW_FLUSH (1<<0)
+#define MSK_ACTIVE_RECEIVER (1<<1)
+
+#include "errcode.h"
+
+#include "config.h"
+
+/* The default RSYNC_RSH is always set in config.h. */
+
+#include
+#ifdef HAVE_SYS_TYPES_H
+# include
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include
+#endif
+#ifdef STDC_HEADERS
+# include
+# include
+#else
+# ifdef HAVE_STDLIB_H
+# include
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+# include
+# endif
+# include
+#endif
+#ifdef HAVE_STRINGS_H
+# include
+#endif
+#ifdef HAVE_INTTYPES_H
+# include
+#endif
+#ifdef HAVE_STDINT_H
+# include
+#endif
+#ifdef HAVE_UNISTD_H
+# include
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include
+#endif
+
+#if defined HAVE_MALLOC_H && (defined HAVE_MALLINFO || !defined HAVE_STDLIB_H)
+#include
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include
+#endif
+
+#ifdef TIME_WITH_SYS_TIME
+#include
+#include
+#else
+#ifdef HAVE_SYS_TIME_H
+#include
+#else
+#include
+#endif
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include
+#else
+#ifdef HAVE_SYS_FCNTL_H
+#include
+#endif
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+#include
+#endif
+
+#ifdef HAVE_SYS_FILIO_H
+#include
+#endif
+
+#include
+#ifdef HAVE_SYS_WAIT_H
+#include
+#endif
+#ifdef HAVE_CTYPE_H
+#include
+#endif
+#ifdef HAVE_GRP_H
+#include
+#endif
+#include
+
+#ifdef HAVE_UTIME_H
+#include
+#endif
+
+#if defined HAVE_UTIMENSAT || defined HAVE_LUTIMES
+#define CAN_SET_SYMLINK_TIMES 1
+#endif
+
+#if defined HAVE_LCHOWN || defined CHOWN_MODIFIES_SYMLINK
+#define CAN_CHOWN_SYMLINK 1
+#endif
+
+#if defined HAVE_LCHMOD || defined HAVE_SETATTRLIST
+#define CAN_CHMOD_SYMLINK 1
+#endif
+
+#ifdef HAVE_UTIMENSAT
+#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+#define ST_MTIME_NSEC st_mtim.tv_nsec
+#elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
+#define ST_MTIME_NSEC st_mtimensec
+#endif
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include
+#endif
+
+#ifdef HAVE_SYS_MODE_H
+/* apparently AIX needs this for S_ISLNK */
+#ifndef S_ISLNK
+#include
+#endif
+#endif
+
+/* these are needed for the uid/gid mapping code */
+#include
+#include
+
+#include
+#include
+#include
+#ifdef HAVE_NETDB_H
+#include
+#endif
+#include
+#include
+
+#ifdef HAVE_DIRENT_H
+# include
+#else
+# define dirent direct
+# ifdef HAVE_SYS_NDIR_H
+# include
+# endif
+# ifdef HAVE_SYS_DIR_H
+# include
+# endif
+# ifdef HAVE_NDIR_H
+# include
+# endif
+#endif
+
+#ifdef MAJOR_IN_MKDEV
+#include
+# if !defined makedev && (defined mkdev || defined _WIN32 || defined __WIN32__)
+# define makedev mkdev
+# endif
+#elif defined MAJOR_IN_SYSMACROS
+#include
+#endif
+
+#ifdef MAKEDEV_TAKES_3_ARGS
+#define MAKEDEV(devmajor,devminor) makedev(0,devmajor,devminor)
+#else
+#define MAKEDEV(devmajor,devminor) makedev(devmajor,devminor)
+#endif
+
+#ifdef HAVE_COMPAT_H
+#include
+#endif
+
+#ifdef HAVE_LIMITS_H
+# include
+#endif
+
+#if defined USE_ICONV_OPEN && defined HAVE_ICONV_H
+#include
+#ifndef ICONV_CONST
+#define ICONV_CONST
+#endif
+#else
+#ifdef ICONV_CONST
+#undef ICONV_CONST
+#endif
+#ifdef ICONV_OPTION
+#undef ICONV_OPTION
+#endif
+#ifdef iconv_t
+#undef iconv_t
+#endif
+#define iconv_t int
+#endif
+
+#include
+
+#include "lib/pool_alloc.h"
+
+#ifndef HAVE_ID_T
+typedef unsigned int id_t;
+#endif
+#ifndef HAVE_PID_T
+typedef int pid_t;
+#endif
+#ifndef HAVE_MODE_T
+typedef unsigned int mode_t;
+#endif
+#ifndef HAVE_OFF_T
+typedef long off_t;
+#undef SIZEOF_OFF_T
+#define SIZEOF_OFF_T SIZEOF_LONG
+#endif
+#ifndef HAVE_SIZE_T
+typedef unsigned int size_t;
+#endif
+
+#define BOOL int
+
+#ifndef uchar
+#define uchar unsigned char
+#endif
+
+#ifdef SIGNED_CHAR_OK
+#define schar signed char
+#else
+#define schar char
+#endif
+
+#ifndef int16
+#if SIZEOF_INT16_T == 2
+# define int16 int16_t
+#else
+# define int16 short
+#endif
+#endif
+
+#ifndef uint16
+#if SIZEOF_UINT16_T == 2
+# define uint16 uint16_t
+#else
+# define uint16 unsigned int16
+#endif
+#endif
+
+/* Find a variable that is either exactly 32-bits or longer.
+ * If some code depends on 32-bit truncation, it will need to
+ * take special action in a "#if SIZEOF_INT32 > 4" section. */
+#ifndef int32
+#if SIZEOF_INT32_T == 4
+# define int32 int32_t
+# define SIZEOF_INT32 4
+#elif SIZEOF_INT == 4
+# define int32 int
+# define SIZEOF_INT32 4
+#elif SIZEOF_LONG == 4
+# define int32 long
+# define SIZEOF_INT32 4
+#elif SIZEOF_SHORT == 4
+# define int32 short
+# define SIZEOF_INT32 4
+#elif SIZEOF_INT > 4
+# define int32 int
+# define SIZEOF_INT32 SIZEOF_INT
+#elif SIZEOF_LONG > 4
+# define int32 long
+# define SIZEOF_INT32 SIZEOF_LONG
+#else
+# error Could not find a 32-bit integer variable
+#endif
+#else
+# define SIZEOF_INT32 4
+#endif
+
+#ifndef uint32
+#if SIZEOF_UINT32_T == 4
+# define uint32 uint32_t
+#else
+# define uint32 unsigned int32
+#endif
+#endif
+
+#if SIZEOF_OFF_T == 8 || !SIZEOF_OFF64_T || !defined HAVE_STRUCT_STAT64
+#define OFF_T off_t
+#define STRUCT_STAT struct stat
+#define SIZEOF_CAPITAL_OFF_T SIZEOF_OFF_T
+#else
+#define OFF_T off64_t
+#define STRUCT_STAT struct stat64
+#define USE_STAT64_FUNCS 1
+#define SIZEOF_CAPITAL_OFF_T SIZEOF_OFF64_T
+#endif
+
+/* CAVEAT: on some systems, int64 will really be a 32-bit integer IFF
+ * that's the maximum size the file system can handle and there is no
+ * 64-bit type available. The rsync source must therefore take steps
+ * to ensure that any code that really requires a 64-bit integer has
+ * it (e.g. the checksum code uses two 32-bit integers for its 64-bit
+ * counter). */
+#if SIZEOF_INT64_T == 8
+# define int64 int64_t
+# define SIZEOF_INT64 8
+#elif SIZEOF_LONG == 8
+# define int64 long
+# define SIZEOF_INT64 8
+#elif SIZEOF_INT == 8
+# define int64 int
+# define SIZEOF_INT64 8
+#elif SIZEOF_LONG_LONG == 8
+# define int64 long long
+# define SIZEOF_INT64 8
+#elif SIZEOF_OFF64_T == 8
+# define int64 off64_t
+# define SIZEOF_INT64 8
+#elif SIZEOF_OFF_T == 8
+# define int64 off_t
+# define SIZEOF_INT64 8
+#elif SIZEOF_INT > 8
+# define int64 int
+# define SIZEOF_INT64 SIZEOF_INT
+#elif SIZEOF_LONG > 8
+# define int64 long
+# define SIZEOF_INT64 SIZEOF_LONG
+#elif SIZEOF_LONG_LONG > 8
+# define int64 long long
+# define SIZEOF_INT64 SIZEOF_LONG_LONG
+#else
+/* As long as it gets... */
+# define int64 off_t
+# define SIZEOF_INT64 SIZEOF_OFF_T
+#endif
+
+struct hashtable {
+ void *nodes;
+ int32 size, entries;
+ uint32 node_size;
+ short key64;
+};
+
+struct ht_int32_node {
+ void *data;
+ int32 key;
+};
+
+struct ht_int64_node {
+ void *data;
+ int64 key;
+};
+
+#define HT_NODE(tbl, bkts, i) ((void*)((char*)(bkts) + (i)*(tbl)->node_size))
+#define HT_KEY(node, k64) ((k64)? ((struct ht_int64_node*)(node))->key \
+ : (int64)((struct ht_int32_node*)(node))->key)
+
+#ifndef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
+#ifndef MAX
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#endif
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 256
+#endif
+
+#define SUM_LENGTH 16
+#define SHORT_SUM_LENGTH 2
+#define BLOCKSUM_BIAS 10
+
+#ifndef MAXPATHLEN
+#define MAXPATHLEN 1024
+#endif
+
+/* We want a roomy line buffer that can hold more than MAXPATHLEN,
+ * and significantly more than an overly short MAXPATHLEN. */
+#if MAXPATHLEN < 4096
+#define BIGPATHBUFLEN (4096+1024)
+#else
+#define BIGPATHBUFLEN (MAXPATHLEN+1024)
+#endif
+
+#ifndef NAME_MAX
+#define NAME_MAX 255
+#endif
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+#ifndef IN_LOOPBACKNET
+#define IN_LOOPBACKNET 127
+#endif
+
+#if HAVE_UNIXWARE_ACLS|HAVE_SOLARIS_ACLS|HAVE_HPUX_ACLS
+#define ACLS_NEED_MASK 1
+#endif
+
+#if defined HAVE_FALLOCATE || HAVE_SYS_FALLOCATE
+#ifdef HAVE_LINUX_FALLOC_H
+#include
+#endif
+#ifdef FALLOC_FL_KEEP_SIZE
+#define SUPPORT_PREALLOCATION 1
+#elif defined HAVE_FTRUNCATE
+#define SUPPORT_PREALLOCATION 1
+#define PREALLOCATE_NEEDS_TRUNCATE 1
+#endif
+#else /* !fallocate */
+#if defined HAVE_EFFICIENT_POSIX_FALLOCATE && defined HAVE_FTRUNCATE
+#define SUPPORT_PREALLOCATION 1
+#define PREALLOCATE_NEEDS_TRUNCATE 1
+#endif
+#endif
+
+union file_extras {
+ int32 num;
+ uint32 unum;
+};
+
+struct file_struct {
+ const char *dirname; /* The dir info inside the transfer */
+ time_t modtime; /* When the item was last modified */
+ uint32 len32; /* Lowest 32 bits of the file's length */
+ uint16 mode; /* The item's type and permissions */
+ uint16 flags; /* The FLAG_* bits for this item */
+ const char basename[1]; /* The basename (AKA filename) follows */
+};
+
+extern int file_extra_cnt;
+extern int inc_recurse;
+extern int uid_ndx;
+extern int gid_ndx;
+extern int acls_ndx;
+extern int xattrs_ndx;
+
+#define FILE_STRUCT_LEN (offsetof(struct file_struct, basename))
+#define EXTRA_LEN (sizeof (union file_extras))
+#define PTR_EXTRA_CNT ((sizeof (char *) + EXTRA_LEN - 1) / EXTRA_LEN)
+#define DEV_EXTRA_CNT 2
+#define DIRNODE_EXTRA_CNT 3
+#define SUM_EXTRA_CNT ((MAX_DIGEST_LEN + EXTRA_LEN - 1) / EXTRA_LEN)
+
+#define REQ_EXTRA(f,ndx) ((union file_extras*)(f) - (ndx))
+#define OPT_EXTRA(f,bump) ((union file_extras*)(f) - file_extra_cnt - 1 - (bump))
+
+#define NSEC_BUMP(f) ((f)->flags & FLAG_MOD_NSEC ? 1 : 0)
+#define LEN64_BUMP(f) ((f)->flags & FLAG_LENGTH64 ? 1 : 0)
+#define START_BUMP(f) (NSEC_BUMP(f) + LEN64_BUMP(f))
+#define HLINK_BUMP(f) ((f)->flags & (FLAG_HLINKED|FLAG_HLINK_DONE) ? inc_recurse+1 : 0)
+#define ACL_BUMP(f) (acls_ndx ? 1 : 0)
+
+/* The length applies to all items. */
+#if SIZEOF_INT64 < 8
+#define F_LENGTH(f) ((int64)(f)->len32)
+#else
+#define F_LENGTH(f) ((int64)(f)->len32 + ((f)->flags & FLAG_LENGTH64 \
+ ? (int64)OPT_EXTRA(f, NSEC_BUMP(f))->unum << 32 : 0))
+#endif
+
+#define F_MOD_NSEC(f) ((f)->flags & FLAG_MOD_NSEC ? OPT_EXTRA(f, 0)->unum : 0)
+
+/* If there is a symlink string, it is always right after the basename */
+#define F_SYMLINK(f) ((f)->basename + strlen((f)->basename) + 1)
+
+/* The sending side always has this available: */
+#define F_PATHNAME(f) (*(const char**)REQ_EXTRA(f, PTR_EXTRA_CNT))
+
+/* The receiving side always has this available: */
+#define F_DEPTH(f) REQ_EXTRA(f, 1)->num
+
+/* When the associated option is on, all entries will have these present: */
+#define F_OWNER(f) REQ_EXTRA(f, uid_ndx)->unum
+#define F_GROUP(f) REQ_EXTRA(f, gid_ndx)->unum
+#define F_ACL(f) REQ_EXTRA(f, acls_ndx)->num
+#define F_XATTR(f) REQ_EXTRA(f, xattrs_ndx)->num
+#define F_NDX(f) REQ_EXTRA(f, unsort_ndx)->num
+
+/* These items are per-entry optional: */
+#define F_HL_GNUM(f) OPT_EXTRA(f, START_BUMP(f))->num /* non-dirs */
+#define F_HL_PREV(f) OPT_EXTRA(f, START_BUMP(f)+inc_recurse)->num /* non-dirs */
+#define F_DIR_NODE_P(f) (&OPT_EXTRA(f, START_BUMP(f) \
+ + DIRNODE_EXTRA_CNT - 1)->num) /* sender dirs */
+#define F_DIR_RELNAMES_P(f) (&OPT_EXTRA(f, START_BUMP(f) + DIRNODE_EXTRA_CNT \
+ + PTR_EXTRA_CNT - 1)->num) /* sender dirs */
+#define F_DIR_DEFACL(f) OPT_EXTRA(f, START_BUMP(f))->unum /* receiver dirs */
+#define F_DIR_DEV_P(f) (&OPT_EXTRA(f, START_BUMP(f) + ACL_BUMP(f) \
+ + DEV_EXTRA_CNT - 1)->unum) /* receiver dirs */
+
+/* This optional item might follow an F_HL_*() item. */
+#define F_RDEV_P(f) (&OPT_EXTRA(f, START_BUMP(f) + HLINK_BUMP(f) + DEV_EXTRA_CNT - 1)->unum)
+
+/* The sum is only present on regular files. */
+#define F_SUM(f) ((char*)OPT_EXTRA(f, START_BUMP(f) + HLINK_BUMP(f) \
+ + SUM_EXTRA_CNT - 1))
+
+/* Some utility defines: */
+#define F_IS_ACTIVE(f) (f)->basename[0]
+#define F_IS_HLINKED(f) ((f)->flags & FLAG_HLINKED)
+
+#define F_HLINK_NOT_FIRST(f) BITS_SETnUNSET((f)->flags, FLAG_HLINKED, FLAG_HLINK_FIRST)
+#define F_HLINK_NOT_LAST(f) BITS_SETnUNSET((f)->flags, FLAG_HLINKED, FLAG_HLINK_LAST)
+
+/* These access the F_DIR_DEV_P() and F_RDEV_P() values: */
+#define DEV_MAJOR(a) (a)[0]
+#define DEV_MINOR(a) (a)[1]
+
+/* These access the F_DIRS_NODE_P() values: */
+#define DIR_PARENT(a) (a)[0]
+#define DIR_FIRST_CHILD(a) (a)[1]
+#define DIR_NEXT_SIBLING(a) (a)[2]
+
+#define IS_MISSING_FILE(statbuf) ((statbuf).st_mode == 0)
+
+/*
+ * Start the flist array at FLIST_START entries and grow it
+ * by doubling until FLIST_LINEAR then grow by FLIST_LINEAR
+ */
+#define FLIST_START (32 * 1024)
+#define FLIST_LINEAR (FLIST_START * 512)
+
+/*
+ * Extent size for allocation pools: A minimum size of 128KB
+ * is needed to mmap them so that freeing will release the
+ * space to the OS.
+ *
+ * Larger sizes reduce leftover fragments and speed free calls
+ * (when they happen). Smaller sizes increase the chance of
+ * freed allocations freeing whole extents.
+ */
+#define NORMAL_EXTENT (256 * 1024)
+#define SMALL_EXTENT (128 * 1024)
+
+#define FLIST_TEMP (1<<1)
+
+struct file_list {
+ struct file_list *next, *prev;
+ struct file_struct **files, **sorted;
+ alloc_pool_t file_pool;
+ void *pool_boundary;
+ int used, malloced;
+ int low, high; /* 0-relative index values excluding empties */
+ int ndx_start; /* the start offset for inc_recurse mode */
+ int flist_num; /* 1-relative file_list number or 0 */
+ int parent_ndx; /* dir_flist index of parent directory */
+ int in_progress, to_redo;
+};
+
+#define SUMFLG_SAME_OFFSET (1<<0)
+
+struct sum_buf {
+ OFF_T offset; /**< offset in file of this chunk */
+ int32 len; /**< length of chunk of file */
+ uint32 sum1; /**< simple checksum */
+ int32 chain; /**< next hash-table collision */
+ short flags; /**< flag bits */
+ char sum2[SUM_LENGTH]; /**< checksum */
+};
+
+struct sum_struct {
+ OFF_T flength; /**< total file length */
+ struct sum_buf *sums; /**< points to info for each chunk */
+ int32 count; /**< how many chunks */
+ int32 blength; /**< block_length */
+ int32 remainder; /**< flength % block_length */
+ int s2length; /**< sum2_length */
+};
+
+struct map_struct {
+ OFF_T file_size; /* File size (from stat) */
+ OFF_T p_offset; /* Window start */
+ OFF_T p_fd_offset; /* offset of cursor in fd ala lseek */
+ char *p; /* Window pointer */
+ int32 p_size; /* Largest window size we allocated */
+ int32 p_len; /* Latest (rounded) window size */
+ int32 def_window_size; /* Default window size */
+ int fd; /* File Descriptor */
+ int status; /* first errno from read errors */
+};
+
+#define FILTRULE_WILD (1<<0) /* pattern has '*', '[', and/or '?' */
+#define FILTRULE_WILD2 (1<<1) /* pattern has '**' */
+#define FILTRULE_WILD2_PREFIX (1<<2) /* pattern starts with "**" */
+#define FILTRULE_WILD3_SUFFIX (1<<3) /* pattern ends with "***" */
+#define FILTRULE_ABS_PATH (1<<4) /* path-match on absolute path */
+#define FILTRULE_INCLUDE (1<<5) /* this is an include, not an exclude */
+#define FILTRULE_DIRECTORY (1<<6) /* this matches only directories */
+#define FILTRULE_WORD_SPLIT (1<<7) /* split rules on whitespace */
+#define FILTRULE_NO_INHERIT (1<<8) /* don't inherit these rules */
+#define FILTRULE_NO_PREFIXES (1<<9) /* parse no prefixes from patterns */
+#define FILTRULE_MERGE_FILE (1<<10)/* specifies a file to merge */
+#define FILTRULE_PERDIR_MERGE (1<<11)/* merge-file is searched per-dir */
+#define FILTRULE_EXCLUDE_SELF (1<<12)/* merge-file name should be excluded */
+#define FILTRULE_FINISH_SETUP (1<<13)/* per-dir merge file needs setup */
+#define FILTRULE_NEGATE (1<<14)/* rule matches when pattern does not */
+#define FILTRULE_CVS_IGNORE (1<<15)/* rule was -C or :C */
+#define FILTRULE_SENDER_SIDE (1<<16)/* rule applies to the sending side */
+#define FILTRULE_RECEIVER_SIDE (1<<17)/* rule applies to the receiving side */
+#define FILTRULE_CLEAR_LIST (1<<18)/* this item is the "!" token */
+#define FILTRULE_PERISHABLE (1<<19)/* perishable if parent dir goes away */
+
+#define FILTRULES_SIDES (FILTRULE_SENDER_SIDE | FILTRULE_RECEIVER_SIDE)
+
+typedef struct filter_struct {
+ struct filter_struct *next;
+ char *pattern;
+ uint32 rflags;
+ union {
+ int slash_cnt;
+ struct filter_list_struct *mergelist;
+ } u;
+} filter_rule;
+
+typedef struct filter_list_struct {
+ filter_rule *head;
+ filter_rule *tail;
+ filter_rule *parent_dirscan_head;
+ char *debug_type;
+} filter_rule_list;
+
+struct stats {
+ int64 total_size;
+ int64 total_transferred_size;
+ int64 total_written;
+ int64 total_read;
+ int64 literal_data;
+ int64 matched_data;
+ int64 flist_buildtime;
+ int64 flist_xfertime;
+ int64 flist_size;
+ int num_files, num_dirs, num_symlinks, num_devices, num_specials;
+ int created_files, created_dirs, created_symlinks, created_devices, created_specials;
+ int deleted_files, deleted_dirs, deleted_symlinks, deleted_devices, deleted_specials;
+ int xferred_files;
+};
+
+struct chmod_mode_struct;
+
+struct flist_ndx_item {
+ struct flist_ndx_item *next;
+ int ndx;
+};
+
+typedef struct {
+ struct flist_ndx_item *head, *tail;
+} flist_ndx_list;
+
+#define EMPTY_ITEM_LIST {NULL, 0, 0}
+
+typedef struct {
+ void *items;
+ size_t count;
+ size_t malloced;
+} item_list;
+
+#define EXPAND_ITEM_LIST(lp, type, incr) \
+ (type*)expand_item_list(lp, sizeof (type), #type, incr)
+
+#define EMPTY_XBUF {NULL, 0, 0, 0}
+
+typedef struct {
+ char *buf;
+ size_t pos; /* pos = read pos in the buf */
+ size_t len; /* len = chars following pos */
+ size_t size; /* size = total space in buf */
+} xbuf;
+
+#define INIT_XBUF(xb, str, ln, sz) (xb).buf = (str), (xb).len = (ln), (xb).size = (sz), (xb).pos = 0
+#define INIT_XBUF_STRLEN(xb, str) (xb).buf = (str), (xb).len = strlen((xb).buf), (xb).size = (size_t)-1, (xb).pos = 0
+/* This one is used to make an output xbuf based on a char[] buffer: */
+#define INIT_CONST_XBUF(xb, bf) (xb).buf = (bf), (xb).size = sizeof (bf), (xb).len = (xb).pos = 0
+
+#define ICB_EXPAND_OUT (1<<0)
+#define ICB_INCLUDE_BAD (1<<1)
+#define ICB_INCLUDE_INCOMPLETE (1<<2)
+#define ICB_CIRCULAR_OUT (1<<3)
+#define ICB_INIT (1<<4)
+
+#define IOBUF_KEEP_BUFS 0
+#define IOBUF_FREE_BUFS 1
+
+#define MPLX_SWITCHING IOBUF_KEEP_BUFS
+#define MPLX_ALL_DONE IOBUF_FREE_BUFS
+#define MPLX_TO_BUFFERED 2
+
+#define RL_EOL_NULLS (1<<0)
+#define RL_DUMP_COMMENTS (1<<1)
+#define RL_CONVERT (1<<2)
+
+typedef struct {
+ char name_type;
+ char fname[1]; /* has variable size */
+} relnamecache;
+
+#include "byteorder.h"
+#include "lib/mdigest.h"
+#include "lib/wildmatch.h"
+#include "lib/permstring.h"
+#include "lib/addrinfo.h"
+
+#ifndef __GNUC__
+#define __attribute__(x)
+#else
+# if __GNUC__ <= 2
+# define NORETURN
+# endif
+#endif
+
+#define UNUSED(x) x __attribute__((__unused__))
+#ifndef NORETURN
+#define NORETURN __attribute__((__noreturn__))
+#endif
+
+typedef struct {
+ STRUCT_STAT st;
+#ifdef SUPPORT_ACLS
+ struct rsync_acl *acc_acl; /* access ACL */
+ struct rsync_acl *def_acl; /* default ACL */
+#endif
+#ifdef SUPPORT_XATTRS
+ item_list *xattr;
+#endif
+} stat_x;
+
+#define ACL_READY(sx) ((sx).acc_acl != NULL)
+#define XATTR_READY(sx) ((sx).xattr != NULL)
+
+#include "proto.h"
+
+#ifndef SUPPORT_XATTRS
+#define x_stat(fn,fst,xst) do_stat(fn,fst)
+#define x_lstat(fn,fst,xst) do_lstat(fn,fst)
+#define x_fstat(fd,fst,xst) do_fstat(fd,fst)
+#endif
+
+/* We have replacement versions of these if they're missing. */
+#ifndef HAVE_ASPRINTF
+int asprintf(char **ptr, const char *format, ...);
+#endif
+
+#ifndef HAVE_VASPRINTF
+int vasprintf(char **ptr, const char *format, va_list ap);
+#endif
+
+#if !defined HAVE_VSNPRINTF || !defined HAVE_C99_VSNPRINTF
+#define vsnprintf rsync_vsnprintf
+int vsnprintf(char *str, size_t count, const char *fmt, va_list args);
+#endif
+
+#if !defined HAVE_SNPRINTF || !defined HAVE_C99_VSNPRINTF
+#define snprintf rsync_snprintf
+int snprintf(char *str, size_t count, const char *fmt,...);
+#endif
+
+
+#ifndef HAVE_STRERROR
+extern char *sys_errlist[];
+#define strerror(i) sys_errlist[i]
+#endif
+
+#ifndef HAVE_STRCHR
+# define strchr index
+# define strrchr rindex
+#endif
+
+#ifndef HAVE_ERRNO_DECL
+extern int errno;
+#endif
+
+#ifdef HAVE_READLINK
+#define SUPPORT_LINKS 1
+#if !defined NO_SYMLINK_XATTRS && !defined NO_SYMLINK_USER_XATTRS
+#define do_readlink(path, buf, bufsiz) readlink(path, buf, bufsiz)
+#endif
+#endif
+#ifdef HAVE_LINK
+#define SUPPORT_HARD_LINKS 1
+#endif
+
+#ifdef HAVE_SIGACTION
+#define SIGACTION(n,h) sigact.sa_handler=(h), sigaction((n),&sigact,NULL)
+#define signal(n,h) we_need_to_call_SIGACTION_not_signal(n,h)
+#else
+#define SIGACTION(n,h) signal(n,h)
+#endif
+
+#ifndef EWOULDBLOCK
+#define EWOULDBLOCK EAGAIN
+#endif
+
+#ifndef STDIN_FILENO
+#define STDIN_FILENO 0
+#endif
+
+#ifndef STDOUT_FILENO
+#define STDOUT_FILENO 1
+#endif
+
+#ifndef STDERR_FILENO
+#define STDERR_FILENO 2
+#endif
+
+#ifndef S_IRUSR
+#define S_IRUSR 0400
+#endif
+
+#ifndef S_IWUSR
+#define S_IWUSR 0200
+#endif
+
+#ifndef ACCESSPERMS
+#define ACCESSPERMS 0777
+#endif
+
+#ifndef S_ISVTX
+#define S_ISVTX 0
+#endif
+
+#define CHMOD_BITS (S_ISUID | S_ISGID | S_ISVTX | ACCESSPERMS)
+
+#ifndef _S_IFMT
+#define _S_IFMT 0170000
+#endif
+
+#ifndef _S_IFLNK
+#define _S_IFLNK 0120000
+#endif
+
+#ifndef S_ISLNK
+#define S_ISLNK(mode) (((mode) & (_S_IFMT)) == (_S_IFLNK))
+#endif
+
+#ifndef S_ISBLK
+#define S_ISBLK(mode) (((mode) & (_S_IFMT)) == (_S_IFBLK))
+#endif
+
+#ifndef S_ISCHR
+#define S_ISCHR(mode) (((mode) & (_S_IFMT)) == (_S_IFCHR))
+#endif
+
+#ifndef S_ISSOCK
+#ifdef _S_IFSOCK
+#define S_ISSOCK(mode) (((mode) & (_S_IFMT)) == (_S_IFSOCK))
+#else
+#define S_ISSOCK(mode) (0)
+#endif
+#endif
+
+#ifndef S_ISFIFO
+#ifdef _S_IFIFO
+#define S_ISFIFO(mode) (((mode) & (_S_IFMT)) == (_S_IFIFO))
+#else
+#define S_ISFIFO(mode) (0)
+#endif
+#endif
+
+#ifndef S_ISDIR
+#define S_ISDIR(mode) (((mode) & (_S_IFMT)) == (_S_IFDIR))
+#endif
+
+#ifndef S_ISREG
+#define S_ISREG(mode) (((mode) & (_S_IFMT)) == (_S_IFREG))
+#endif
+
+/* work out what fcntl flag to use for non-blocking */
+#ifdef O_NONBLOCK
+# define NONBLOCK_FLAG O_NONBLOCK
+#elif defined SYSV
+# define NONBLOCK_FLAG O_NDELAY
+#else
+# define NONBLOCK_FLAG FNDELAY
+#endif
+
+#ifndef INADDR_LOOPBACK
+#define INADDR_LOOPBACK 0x7f000001
+#endif
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+#define IS_SPECIAL(mode) (S_ISSOCK(mode) || S_ISFIFO(mode))
+#define IS_DEVICE(mode) (S_ISCHR(mode) || S_ISBLK(mode))
+
+#define PRESERVE_FILE_TIMES (1<<0)
+#define PRESERVE_DIR_TIMES (1<<1)
+#define PRESERVE_LINK_TIMES (1<<2)
+
+/* Initial mask on permissions given to temporary files. Mask off setuid
+ bits and group access because of potential race-condition security
+ holes, and mask other access because mode 707 is bizarre */
+#define INITACCESSPERMS 0700
+
+/* handler for null strings in printf format */
+#define NS(s) ((s)?(s):"")
+
+/* Convenient wrappers for malloc and realloc. Use them. */
+#define new(type) ((type*)malloc(sizeof (type)))
+#define new0(type) ((type*)calloc(1, sizeof (type)))
+#define new_array(type, num) ((type*)_new_array((num), sizeof (type), 0))
+#define new_array0(type, num) ((type*)_new_array((num), sizeof (type), 1))
+#define realloc_array(ptr, type, num) ((type*)_realloc_array((ptr), sizeof(type), (num)))
+
+/* use magic gcc attributes to catch format errors */
+ void rprintf(enum logcode , const char *, ...)
+ __attribute__((format (printf, 2, 3)))
+;
+
+/* This is just like rprintf, but it also tries to print some
+ * representation of the error code. Normally errcode = errno. */
+void rsyserr(enum logcode, int, const char *, ...)
+ __attribute__((format (printf, 3, 4)))
+ ;
+
+/* Make sure that the O_BINARY flag is defined. */
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#ifndef HAVE_STRLCPY
+size_t strlcpy(char *d, const char *s, size_t bufsize);
+#endif
+
+#ifndef HAVE_STRLCAT
+size_t strlcat(char *d, const char *s, size_t bufsize);
+#endif
+
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(stat) ((int)(((stat)>>8)&0xFF))
+#endif
+#ifndef WIFEXITED
+#define WIFEXITED(stat) ((int)((stat)&0xFF) == 0)
+#endif
+
+#define exit_cleanup(code) _exit_cleanup(code, __FILE__, __LINE__)
+
+#ifdef HAVE_GETEUID
+#define MY_UID() geteuid()
+#else
+#define MY_UID() getuid()
+#endif
+
+#ifdef HAVE_GETEGID
+#define MY_GID() getegid()
+#else
+#define MY_GID() getgid()
+#endif
+
+#ifdef FORCE_FD_ZERO_MEMSET
+#undef FD_ZERO
+#define FD_ZERO(fdsetp) memset(fdsetp, 0, sizeof (fd_set))
+#endif
+
+extern short info_levels[], debug_levels[];
+
+#define INFO_GTE(flag, lvl) (info_levels[INFO_##flag] >= (lvl))
+#define INFO_EQ(flag, lvl) (info_levels[INFO_##flag] == (lvl))
+#define DEBUG_GTE(flag, lvl) (debug_levels[DEBUG_##flag] >= (lvl))
+#define DEBUG_EQ(flag, lvl) (debug_levels[DEBUG_##flag] == (lvl))
+
+#define INFO_BACKUP 0
+#define INFO_COPY (INFO_BACKUP+1)
+#define INFO_DEL (INFO_COPY+1)
+#define INFO_FLIST (INFO_DEL+1)
+#define INFO_MISC (INFO_FLIST+1)
+#define INFO_MOUNT (INFO_MISC+1)
+#define INFO_NAME (INFO_MOUNT+1)
+#define INFO_PROGRESS (INFO_NAME+1)
+#define INFO_REMOVE (INFO_PROGRESS+1)
+#define INFO_SKIP (INFO_REMOVE+1)
+#define INFO_STATS (INFO_SKIP+1)
+#define INFO_SYMSAFE (INFO_STATS+1)
+
+#define COUNT_INFO (INFO_SYMSAFE+1)
+
+#define DEBUG_ACL 0
+#define DEBUG_BACKUP (DEBUG_ACL+1)
+#define DEBUG_BIND (DEBUG_BACKUP+1)
+#define DEBUG_CHDIR (DEBUG_BIND+1)
+#define DEBUG_CONNECT (DEBUG_CHDIR+1)
+#define DEBUG_CMD (DEBUG_CONNECT+1)
+#define DEBUG_DEL (DEBUG_CMD+1)
+#define DEBUG_DELTASUM (DEBUG_DEL+1)
+#define DEBUG_DUP (DEBUG_DELTASUM+1)
+#define DEBUG_EXIT (DEBUG_DUP+1)
+#define DEBUG_FILTER (DEBUG_EXIT+1)
+#define DEBUG_FLIST (DEBUG_FILTER+1)
+#define DEBUG_FUZZY (DEBUG_FLIST+1)
+#define DEBUG_GENR (DEBUG_FUZZY+1)
+#define DEBUG_HASH (DEBUG_GENR+1)
+#define DEBUG_HLINK (DEBUG_HASH+1)
+#define DEBUG_ICONV (DEBUG_HLINK+1)
+#define DEBUG_IO (DEBUG_ICONV+1)
+#define DEBUG_OWN (DEBUG_IO+1)
+#define DEBUG_PROTO (DEBUG_OWN+1)
+#define DEBUG_RECV (DEBUG_PROTO+1)
+#define DEBUG_SEND (DEBUG_RECV+1)
+#define DEBUG_TIME (DEBUG_SEND+1)
+
+#define COUNT_DEBUG (DEBUG_TIME+1)
+
+#ifndef HAVE_INET_NTOP
+const char *inet_ntop(int af, const void *src, char *dst, size_t size);
+#endif
+
+#ifndef HAVE_INET_PTON
+int inet_pton(int af, const char *src, void *dst);
+#endif
+
+#ifndef HAVE_GETPASS
+char *getpass(const char *prompt);
+#endif
+
+#ifdef MAINTAINER_MODE
+const char *get_panic_action(void);
+#endif
diff --git a/rsync/rsync3.txt b/rsync/rsync3.txt
new file mode 100644
index 0000000..967aa4b
--- /dev/null
+++ b/rsync/rsync3.txt
@@ -0,0 +1,469 @@
+-*- indented-text -*-
+
+Notes towards a new version of rsync
+Martin Pool