diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..54a20ce --- /dev/null +++ b/AUTHORS @@ -0,0 +1,8 @@ + +Valient Gough + + +Also, thanks to the work of many contributors, encfs as of 1.1.11 now has +full or partial translations for many languages. +See README-NLS and TRANSLATORS for more details. + diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/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/ChangeLog b/ChangeLog new file mode 100644 index 0000000..9c421a2 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,1190 @@ +Sun Jan 6 22:26:25 PST 2008 Valient Gough + * fix SSL test to keep -lssl at end of compile line, + found by Kenny Simpson + +Sun Jan 6 22:16:02 PST 2008 Valient Gough + * run extpass command via shell, to allow passing arguments + to program. Patch by Liraz + +Sun Jan 6 21:59:38 PST 2008 Valient Gough + * enlarge max password length + +Sun Jan 6 21:27:33 PST 2008 Valient Gough + * fix build/install scripts + +Sat Jan 5 2008 Valient Gough + * update TRANSLATORS file + * tagged v1.4 + * rename BER config methods to Int + * change license file to GPL3 + +Thu Jan 3 2008 Valient Gough + * include string.h in files using memset + * add mount-on-demand + +Wed Jan 2 2008 Valient Gough + * change openssl engine config + * fix idle tracking by adding usage counter in Context + * move openssl support code to separate file + +Tue Jan 1 2008 Valient Gough + * refuse to unlink open files, in case fuse is running in hard_remove + mode + * fix locking issues from last refactor + * remove global mutex for fsuid. This wasn't synchronizing all cases + anyway, and testing reveals that fsuid is per-thread not per-process + so it wasn't necessary. + * add '-o' option which passes arg through to fuse + * add -h option, patch by Ryan Smith-Roberts + + + + + +Mon Dec 31 2007 Valient Gough + * major changes to DirNode and FileNode. + Move open file tracking into Context. + breaks idle tracking... + * fix rename bug by tracking and applying undo in all error paths. + https://bugs.launchpad.net/bugs/160214 + * bump version 1o 1.4, make ulockmgr optional + * more refactoring of common code + * add ftruncate, fgetattr, lock, utimens functions. + * link move fuse and ulockmgr link specification to configure + +Sun Dec 30 2007 Valient Gough + * keep FileNode reference in fuse_file_info, to avoid path lookups in + related ops move context struct to separate file + * move globals to filesystem-context scope + * fixes to get encfs to compile with libfuse 26 API + +Sat Dec 29 2007 Valient Gough + * remove LinkedOwner, replace with shared_ptr + +Sun Dec 9 2007 Valient Gough + * don't close stderr unless in daemon mode + +Tue Oct 9 2007 Valient Gough + * add locking around calls which set fsuid + * replace custom smart ptr with boost smart ptr + Drops backward compatibility files, to avoid porting/testing ancient + code. Add valgrind support for debugging. + +Mon Mar 19 2007 Valient Gough + * reverse encryption support by Keary Griffin + Adds --reverse option to encfs which causes encfs to produce encrypted + filesystem on-demand, rather then unencrypted. + +Wed Mar 14 2007 Valient Gough + * move buffer management to avoid memory leak in case ofdecoding error + * rename Config -> ConfigReader. Should fix the build problem on + systems with case insensitive filesystems (Mac). + +Mon Feb 12 2007 Valient Gough + * bump library version number + * fix atomic function tests to build on gcc 4.2 + Report and testing from Miklos Szeredi + * bump version to 1.3.2 + +Thu Jan 11 2007 Valient Gough + * make sure cache is correctly cleared in case of failure + Fixes bug reported by Liraz @ sourceforge. + Bug: 1633662 Encfs failing to verify block HMAC + +Tue Jul 25 2006 Valient Gough + * add workaround for group permission problem in 'public' mode + based on patch by Dan Sheridan. If node creation fails due to + permission problems in 'public' filesystem mode, then try using gid + from parent directory. + * add parentDirectory method to FileUtils and FileNode + +Sun Jul 16 2006 Valient Gough + * support for static builds, patch by Marc Zonzon + * check for fuse_new instead of fuse_main + * don't let compiler optimize away references + Functions for dynamically loaded modules are always referenced now so + that the compiler can't optimize them away when doing a static build. + This wasn't a problem until recent improvements in gcc. + +Sat Apr 1 2006 Valient Gough + * track list of new FUSE callbacks which are not implemented in encfs + * bump version to 1.3.1 + +Tue Mar 28 2006 Valient Gough + * skip "." and ".." in recursive rename + +Wed Mar 8 2006 Valient Gough + * tagged v1.3 + * cleanup logging output and close stderr on successful startup of + daemon. Encfs was keeping open stderr after startup, so close it + during encfs_init. + * fix visibility of .dot files when Null filename encryption selected. + Reported by Andrew Schretter, and patched in January, but I forgot to + commit fix. + * minor fixup to int64 cast after patch import + +Mon Mar 6 2006 Valient Gough + * reorder encfs man page place more useful flags first in options list + * update version, check for fuse 2.5 + +Mon Mar 6 2006 Csaba Henk + * port to FreeBSD + * update to FUSE 2.5 API (minimum provided by FUSE for FreeBSD) + +Wed Nov 9 2005 Valient Gough + * add workaround for bogus warning message returned on unmount. + fuse_main seems to be returning an error code even if it exits + cleanly. Only show an error if it returned with an error + immediately. + * update lib version to 1:3:0 + * bump version to 1.2.5 + * fix compiler warnings about printf int sizes on 64bit machine + * update atomic add config check, disable threading if not found + Fixes core dump due to race condition on dual-core system, reported + by Chris of x.nu. + Thanks to bock at blacknet.de for providing access to a system for + testing. + +Mon Sep 12 2005 Valient Gough + * have encfssh make the mountpoint only user readable + +Sun Sep 11 2005 Valient Gough + * add encfssh script + Script to mount, spawn shell in mount, and unmount when shell exits. + Contributed by David Rosenstrauch + +Fri Aug 26 2005 Valient Gough + * add 1.2.4 release notes to spec + * have configure generate po/Makefile + * add changelog entries + +Wed Aug 24 2005 Valient Gough + * raw dir may not be a subdirectory of mount point + Fixes issue reported by paulgfx - if raw directory was a subdirectory + of the mount point, this would cause infinite recursion (because each + request by encfs on the raw data would be translated into another + encfs request by the kernel). + * moves the directory creation until the other tests have been made, to + avoid creating directories unless all sanity checks pass. + +Tue Aug 23 2005 Valient Gough + * avoid trying to decipher impossibly small filenames + Bug reported by paulgfx - if a bogus file was placed in the encfs raw + directory with a name that was too small to be a valid encrypted + name, it could cause encfs to segfault. Such files are now skipped. + +Sat Aug 20 2005 Valient Gough + * add support for static build + In static builds, the linker skips objects from the libencfs library + which are never directly referenced. This breaks all encoding and + encryption modules, because they are dynamically registered. The + workaround is to explicitly add references in the library in the + static build case. + +Wed Aug 17 2005 Valient Gough + * bump version to 1.2.4 + * refactor small amount of error code + * add environment override for config file location + If the environment variable 'ENCFS5_CONFIG' is set, then it will be + used as the location for the encfs configuration file. Otherwise, + the normal search will be done, by searching the specified root + directory for an appropriate config file. + +Fri Aug 5 2005 jjhellst@gmail.com + * Remove duplicate initFS and checkDir calls + Remove duplicated calls to initFS by refactoring call to initFS into + initRootInfo function that calls checkDir and initFS. This removes + some of the redundancy in cmd_* handlers. + The creation of destDir in cmd_export is also moved to a later stage, + so that if encfs volume couldn't be initialized, the destination + directory is not created. + +Fri Jul 29 2005 Valient Gough + * replace reconfig.sh commands with simpler set + * try and simplify devel code build + +Wed Jul 27 2005 Valient Gough + * fix up export patch and refactor output code + +Sun Jul 24 2005 Valient Gough + * encfsctl export command patch from Janne Hellsten + This command will export the entire filesystem. + + * move userAllowCreate to userAllowMkdir and make public + * cleanup configure to get rid of warnings + +Wed Jul 13 2005 Valient Gough + * add recent changelog entries + * bump shared object version to 1:2:0 + forgot to do this for the v1.2.3 release + +Mon Jul 11 2005 Valient Gough + tagged v1.2.3 + * bump version to 1.2.3 + * remove lock removal comment + * add debugging output + +Thu Jul 7 2005 Valient Gough + * add way to get filename in FileIO + wanted to know filename for displaying error message, but there was no + way to get it. + * minor cleanup with locking of DirNode & FileNode + doesn't change lock regions or behavior, but cleans it up a bit. + Locks added in constructors even though they aren't necessary in order + to try and get valgrind's helgrind to not report a race there.. but + didn't work + * always warn of MAC comparison failure + if warnonly mode is not turned enabled, then an error will also be + thrown + +Mon Jul 4 2005 Valient Gough + * register nullname io + Once had a request for unencrypted filenames (only data encrypted). + Easy enough by having NullNameIO registered as an option. + * synchronize read path + After fixing the paranoia mode problem in the earlier patch, that seems + to introduce (or exacerbate?) a race condition in the read path. Until + I have time to track it down, the safest action seems to be to + synchronize reads.. + * add 'cat' command to encfsctl + Allows printing out the contents of a file without having to mount the + filesystem. + +Sun Jul 3 2005 Valient Gough + * another fix to allow symbolic links to / + Bug report by neuron. + * allow symbolic link to root directory + +Sat Jul 2 2005 Valient Gough + * don't call setIV on symbolic links + Only call setIV for regular file types and non-existant files. + This is a fix for a problem with symbolic links which was reported to + debian bug tracking system: bug #315624 Affects paranoia mode (IV + chaining + file headers + external IV chaining) + +Mon Jun 20 2005 Valient Gough + * update libtool + +Thu Jun 16 2005 Valient Gough + * initialize OpenSSL hardware engine support + Found out about these calls from LinuxJournal, May 2005 issue + +Sat May 21 2005 Valient Gough + * disable idle monitoring option with fuse 2.2 + * fix build error on rh7.3 + * bump version to 1.2.2 + * tagged v1.2.2 + +Tue May 17 2005 Valient Gough + * fix idle monitor thread + Idle monitor wasn't working in encfs 1.2.x - reported by Patrick + Skerrett on encfs-users mailing list. Problem was that idle thread + needs to be setup after daemon() is called by libfuse, otherwise + thread was being killed. + * increment libencfs version number to 1:1:0 + +Fri May 13 2005 Valient Gough + * add use_ino by default, new flag --no-default-flags + The fuse flags use_ino is now specified by default, since it + shouldn't cost anything. Encfs now has a documented flag + --no-default-flags which can be used to turn off all default fuse + flags. + * add big warning message for external IV chaining + External IV chaining causes known problems with programs like mutt + and procmail, so show a big warning message if it has been enabled, + just so there isn't any surprise. + +Tue May 10 2005 Valient Gough + * fix memory leak on MAC error + When a block MAC error is detected, an exception is thrown, but a + local memory-pool buffer is not released, leading to a leak of a + block. + +Fri May 6 2005 Valient Gough + * add last-block cache + +Sun May 1 2005 Valient Gough + * make FUSE default_permissions flag on by default + * add an 'encode' command in encfsctl + +Wed Apr 20 2005 Valient Gough + * bump version number to 1.2.1-4 + * tagged v1.2.1 + * make return code consistent + +Tue Apr 19 2005 Valient Gough + * fix distribution build + * 2005-04-18/19 updates + * minor cleanup + * use ::open instead of mkstemp for mknod + * use lchown instead of chown after mkdir + +2005-04-19 Valient Gough + * import latest rosetta translation updates + +2005-04-18 Valient Gough + * add "--public" mount option when encfs is run as root, to act as a + standard multi-user filesystem. Based on patch by Sascha Demetrio. + * enable shared library build. Encfs and encfsctl now link to libencfs + shared library. + +2005-04-15 Valient Gough + * provide more scriptable password mechanism, patch from Gerald Klix + * internal development moved to Darcs revision control (because + Bitkeeper folks have shown why open source projects should not rely + upon it). + +2005-04-08 Valient Gough + * build libencfs shared library - + +2005-04-01 Valient Gough + * autosprintf fixes - patch from Andreas Jochens + +2005-03-24 Valient Gough + * import languages files from rosetta + +2005-03-10 Valient Gough + * add openssl license exception statement - requested by debian folks + +2005-02-21 Valient Gough + * drop remainder of old fuse hide + * callback support + +2005-02-10 Valient Gough + * add sample extpass program script + + * eliminate fixed descriptor number when communicating with external + password program. Fixes problem of fixed descriptor number + conflicting with password return socket in some circumstances. + Found by Olivier Dournaux. + + * drop support for older libfuse versions of fuse_main which do not + return an integer error code. + +2005-02-09 Valient Gough + * log debug messages of error conditions to Info channel instead of + Debug + + * fix bug in MAC header code which caused corruption in files > 2GB + Reported by Damian Frank. + +2005-02-05 Valient Gough + * convert to fuse 2.2 api, dropping support for older versions + + * change encfs version to 1.2 + + * add inode number pass-thru support, allowing inode numbers to be the + same as the underlying filesystem. This makes it so that hard links + will have the same inode number (which some programs check).. + +2005-02-04 Valient Gough + * import translations from rosetta, fix formatting + +2005-01-13 Valient Gough + * let libfuse 2.x handle backgrounding itself + +2005-01-12 Valient Gough + * prepare for 1.1.11 r4 release. + * TAG: v1.1.11-4 + + * fix up formatting + * modify Makefile to use 'bk edit' before modifying a .po file + + * import from rosetta + +2005-01-07 Valient Gough + * rosetta update + +2005-01-04 Valient Gough + * configure.ac: + * remove hardcoded -lpthread in fuse check + + * add pthread support code for autoconf from librlog + + * misc updates releated to NLS + * TAG: v1.1.11-3 + + * update from rosetta + +2005-01-03 Valient Gough + * sv.po, pl.po, fr.po, fi.po, es_ES.po, es.po, de_DE.po, de_CH.po: + * updated pot and po files + * fr.po, de_DE.po: + * merge from rosetta + * MACFileIOCompat.cpp: + * change to match same message in another file + + * rosetta translation updates. + * add workaround to problem of fuse_main not returning an error code in + fuse 1.x + +2004-12-29 Valient Gough + * rosetta-download.pl: + * take project name argument + + * import newest Rosetta files + * fix up .po problems. + * add rosetta-download.pl script + * add TRANSLATORS + +2004-12-28 Valient Gough + * add README-NLS + * update fr.po from Rosetta + + * add more po links + * minor changes after test build on RH7.3 + + * boldquot.sed, insert-header.sin, remove-potcdate.sin: + * new file + + * replace various automake files with links + + * import files from m4 directory, so they appear in an export.. + + + * mkinstalldirs: + * new file + * TAG: v1.1.11 + + * add translation files from Rosetta + + * more fixes for RH7.3 build + + * remove dependency on external gettext files + * some fixes to build on RH7.3 + + * NLS updates + * add BUILD_NLS conditional to try and make encfs buildable without + gettext + +2004-12-28 gettextize + + * Makefile.am (ACLOCAL_AMFLAGS): New variable. + * configure.ac (AM_GNU_GETTEXT_VERSION): Bump to 0.14.1. + +2004-12-25 Valient Gough + * po-group: + docs + + * fix processing of last entry in .pot + + * POTFILES.in: + add files + + * Makefile.in.in: + use encfs.pot as the final (sorted) pot. + encfs-raw.pot is the unsorted data + + * sort translation strings by importance + +2004-12-13 Valient Gough + * i18 support + +2004-12-12 Valient Gough + * minor i18n changes + + * initial i18n support + +2004-11-23 Valient Gough + * add -H option to get fuse_mount usage message + +2004-11-08 Valient Gough + * check if fuse_main returns integer. Should allow encfs to build with + older versions of fuse + + * fix fuse argument separation for recent versions of fuse (fix + reported by Bill Cox). + + * fix problems with recursive rename undo (reported by Rorick Olson, + and possibly others). + +2004-11-03 Valient Gough + * lots of cleanup of configuration scripts, since they wouldn't work + with the most recent autoconf tools. + + * fixes for recursive rename which was failing because it expected + directories to have IV headers. + +2004-10-29 Valient Gough + * add flag to allow file holes in BlockFileIO + + * fix for sourceforge bug 1056718 - wrong error code from getxattr + +2004-08-15 Valient Gough + * configure.in: + change version to 1.1.9 + * TAG: v1.1.9 + + * fix for rename() bug in paranoia mode. + +2004-08-14 Valient Gough + * 1.1.8 release + * TAG: v1.1.8 + +2004-08-13 Valient Gough + * Fix some problems with MAC block header processing. Backward + compatibility is maintained for people who were not experiencing + problems. The new code is much faster, and should be more reliable + as well.. + +2004-08-12 Valient Gough + * minor fixes, test for known error conditions early in filename decode + path + + * TAG: v1.1.7-2 + + * add support for forced decoding in the case of MAC checksum failure + +2004-08-11 Valient Gough + * update version and notes for 1.1.7-2 + * TAG: v1.1.7-2 + + * fix error in truncate() on unopened file + + * version change to 1.1.7 + + * fixes to build on RedHat 7.3 (or system with old OpenSSL) fix race + condition in CipherV3 + +2004-08-09 Valient Gough + * Fix bug which would allow multiple files to be created with the same + file IV header. + + * Add mlock calls in case user has permission to lock regions of memory. + + * Add support for IV chaining to old 0.x filesystem support code. This + code is still useful when building on a system that doesn't have a + recent version of OpenSSL (such as RedHat 7.x) + + * TAG: v1.1.6 + +2004-07-22 Valient Gough + * bump version to 1.1.5 + * TAG: v1.1.5 + + * fix incorrect test for completion status after recursive rename. + some minor code cleanup. + +2004-07-10 Valient Gough + * documentation updates + + * changes for version 1.1.4 release + * TAG: v1.1.4 + +2004-07-09 Valient Gough + * add external password prompt support. + + * remove explicit file hiding support, as hidefile operations changes + were not integrated + + * into FUSE (but the libfuse internal handling changes were). + +2004-07-01 Valient Gough + * fix rename warning to print entire path. + * disable libfuse V2's default background mode. + +2004-06-24 Valient Gough + * cleanup for 1.1.3 release - notes in man page, RELEASE tag update.. + * TAG: v1.1.3 + + * hard links cannot be supported with external IV chaining. Return + error if attempted. + + * avoid uninitialized memory warnings + + * add support for filename -> data IV chaining. + +2004-06-23 Valient Gough + * Feature: + add support for fuse_operations.hidefile - which allows + delete-on-open and rename-on-open operation support. This allows + Evolution mail reader to work much better. + + * Security: + don't log plaintext filename in fsync. + + * Other: + use atomic operations if bits/atomicity.h found. + Add more sanity checks with rAssert. + +2004-06-19 Valient Gough + * added atomic lookup/open api to DirNode to simplify logic. + * add ability to store symbolic links to fully qualified path names. + * add ring buffer cache for unopened FileNode entries. + * make locking logic more robust in case of exceptions. + +2004-06-16 Valient Gough + * minor updates + + * some minor updates + +2004-06-08 Valient Gough + * merge RedHat 7.3 build fixes + +2004-06-07 Valient Gough + * fix build problems on RedHat 7.3 system + +2004-05-28 Valient Gough + * documentation updates for 1.1.x. + * add extra includes necessary for some platforms... + + * updates for 1.1.2 release + +2004-05-26 Valient Gough + * work around potential race condition with unlink. + + * add undo to recursive renames to that a failed rename will undo + changes. + +2004-05-24 Valient Gough + * update version to 1.1.2 + + * Fix bug reported by Jens Arm -- change CipherFileIO and MACFileIO to + only adjust size for regular files (not directories, etc). This bug + prevented EncFS from being used on top of an XFS filesystem.. + +2004-05-23 Valient Gough + * add support for decoding a list of names from stdin in encfsctl. + +2004-05-22 Valient Gough + * fix up includes so that extra dependency path discovered in configure + get included.. + +2004-05-20 Valient Gough + * change release to 1.1.1-2 + * fix possible race condition in cipher by locking around use of cipher + state variables. + + * make recursive rename more robust and also propogate changes to + denormalized values. + +2004-05-19 Valient Gough + * change release to 1.1.1-1 + * add release number to tar builds + + * add support for file rename while open, needed for Evolution to work + properly. + + * add variable size blockMAC headers (and random byte components), as + random bytes are not needed when file IV headers are enabled. + + * New V5 configuration subversion - 20040518. + + * add try/catch blocks around most operations in encfs.cpp, as uncaught + exceptions will cause fuse library to exit. + + * add decode command to encfsctl to decode an encrypted path name. + + * fix memory bounds error in MACFileIO + +2004-05-18 Valient Gough + * bump release to 4 + * TAG: v1.1-4 + + * add showcruft command to encfsctl which displays undecodable filenames + + * make recursive rename two stages to avoid trying to rename files more + then once. + + * add recursive directory renaming if directory IV chaining is enabled, + otherwise directory contents are not visible after a rename. + +2004-05-17 Valient Gough + * change password prompt text. + * eliminate harmless uninitialized memory read warning from valgrind. + * TAG: v1.1 + +2004-05-14 Valient Gough + * add padding to conversion array to avoid possible array bounds read + error with invalid filenames + + * allow CipherV3 to be built even if newer SSL interfaces are not + available. + + * add full V3 support (filesystems created by EncFS 0.2 - 0.6) + +2004-05-13 Valient Gough + * documentation updates + + * minor documentation updates + + * add documentation of filesystem options to man page + +2004-05-12 Valient Gough + * add reference counting for open/release calls, since FUSE can have + overlapping retain/release states on a file. + + * fix multiple delete in main exit cleanup. + + * lots of changes to DirNode and FileNode classes to make more thread + safe. The threading problems seemed to be mostly due to the way + FileNodes were created and passed around. + + * add xattr support (untested) since I noticed new calls in the FUSE + library header from CVS. + + * changed open file caching to be based on plaintext name rather then + encrypted name. FUSE passes the plaintext names to EncFS for + processing, so rather then having to encrypt them each time, use the + plaintext name for lookup in the cache. This makes it more likely to + have plaintext names sitting around in memory, so I've tried to have + the names get cleared when they are no longer in use. + +2004-05-04 Valient Gough + * merge FlatDirNode into DirNode. There is no longer a need to have + the base class separate since the purpose was in doing per-directory + initialization vectors, which are no longer necessary now with + filename IV chaining and per-file IV headers. + + * add 64-bit IV chaining implementations to StreamNameIO and + BlockNameIO. + + * add means of finding out current interface of NameIO and Cipher + instances. + +2004-05-03 Valient Gough + * fix build error with current fuse CVS snapshot + +2004-05-01 Valient Gough + * fix checksum decoding bug when IV chaining is not enabled. + +2004-04-30 Valient Gough + * Add filename IV chaining mode. Found an easy way to add per-file + initialization vectors. I thought I was going to have to store + per-directory IV data somewhere (or like CVS, store IV data in + strange parts of file headers), but I found I could do much better + more simply by chaining the initialization vectors during a path + encode/decode since EncFS always knows the full path of the file + being accessed. This adds 64 bits of path-dependent initialization + vector data without any storage overhead. + +2004-04-28 Valient Gough + * Thanks to Anthony Iano-Fletcher for helping test the 1.1 beta. It + was quickly discovered that there was a race condition, so the 1.1 + release is delayed pending fixes. + + * Add support for initialization vector argument to NameIO name + encoding methods. + + * Add mknod method to FileNode, remove blockSize(). + * Fix warnings from valgrind regarding uninitialized memory access. + +2004-04-20 Valient Gough + * TAG: v1.1 + * fix problems found when running rsync, as suggested by Anthony Iano-Fletcher. + +2004-04-17 Valient Gough + * fix assert failure from showFSInfo due to BlockNameIO being + instanciated with null cipher. + +2004-04-16 Valient Gough + * some minor updates - mostly comment additions in preparation for 1.1 release + + * fix uninitialized memory read. + + * change BlockNameIO implementation to include padding bytes in MAC. + * configuration selection and display changes. + +2004-04-15 Valient Gough + * add per-file initialization vector support. Stores 64-bit + initialization vector modifier for each file. The IV vector is + stored encrypted as a transparent header on the file. + + * add a prompt for MAC headers in setup, and show information about + filesystem to user. + + * add option to turn off key validation checking + + * add support for per-block MAC headers which ensure data within a + block cannot be modified without being detected. + +2004-04-14 Valient Gough + * name encoding output for encfsctl and added creator tag to V5 config + file. + + * V5 configuration, now supports block mode filename encryption. + +2004-04-13 Valient Gough + * botan updates. The botan API is a complex beast. I may drop it and + instead focus on adding support for something more likely to be found + on a system - like mcrypt or gcrypt. + +2004-04-12 Valient Gough + * implement Block name encoding. This hides the size of filenames to + some extent. + + * renamed StdNameIO to StreamNameIO. + + * Clean up Cipher API and eliminate special case name + encoding/decoding, which is now part of NameIO logic. + + * Move code from FileNode to FileIO layer to clean up layer separation. + + * Support for version 3 filesystems (created by EncFS 0.x, supported by + 0.x and 1.0.x) has been dropped. + +2004-04-11 Valient Gough + * remove blockSize from static Cipher constructors. + * change name of stream encoding interfaces. + +2004-04-10 Valient Gough + * change to using smart pointer types and OpaqueValue as CipherKey. + memory ownership was starting to become too complex, so I imported a + smart pointer class from 'rel' codebase (where Interface and + LinkedOwner came from). + + * port truncate fix from mainline. Use Mutex::Lock helper class for + locking + +2004-04-09 Valient Gough + * Cipher interface no longer expects a unique short name. + + * FileUtils contains functions for loading / creating filesystem + configuration. + + * First try implementation of an 'ls' command in encfsctl. + +2004-04-08 Valient Gough + * add support for truncate call with new size > old size. + + * trivial changes + +2004-04-08 Valient Gough + * release version 1.0.5 + + * add support for truncate call with new size > old size. + +2004-03-26 Valient Gough + * release version 1.0.4 + + * fix signalling to idle monitoring thread to avoid getting stuck + waiting for it. + +2004-03-25 Valient Gough + * Update comments in README and INSTALL + + * Make RELEASE a config variable. Sets release number in spec files so + that it only needs to be set in once place. + + * Found that 1.x series was slower then 0.x series due to EVP cipher + context usage. Large speed improvements in SSL_Cipher by optimizing + EVP cipher context lifetime. + + * Optimize read and write to go direct to buffer whenever possible. + This is the fastest encfs ever. + + * Don't remove files that are not automatically generated. Caused a + distribution build problem when the admin directory was updated. + Workaround for make dist complaining about Makefile.am.wo + + * configure.in: use standard "yes" instead of "ok" in result message + +2004-03-24 Valient Gough + * Another block padding fix to ensure blocks are padded with '0's + + * Check for a supported libfuse API at configure time. + + * Add support for FUSE_MAJOR_VERSION 2 (the current CVS version of + FUSE), which changes the statfs interface. + + * Update admin code. + +2004-03-21 Valient Gough + * encfsctl.pod: fix comment + +2004-03-19 Valient Gough + * update README with better description of encryption details + +2004-03-18 Valient Gough + * TAG: v1.0.3 + + * fix truncate bug - truncate was seriously broken, and could cause + corruption of a truncated block! + + * Tested filesystem using a perl script which tries to cause as many + edge cases as possible w.r.t. partial blocks.. It showed up the + problem with padding and truncation.. + +2004-03-17 Valient Gough + * main.cpp: + remove old warning for extra args + + * fix padding bug where some blocks were getting padded with random + data instead of with zeros. + + * Remove uncessary padding in the last block of a file, since it gets + overwritten by the write call immediatly afterwards. Should speed up + partial block writes. + + * switch to getopt for argument parsing, and support GNU style long arguments + + * add inactivity timeout support (--idle option) + +2004-03-14 Valient Gough + * TAG: v1.0.2 + * fix compile problems on RedHat 7.3 system + + * configure.in: fix rlog check when pkg-config isn't found + + * makedist2.sh.in: remove suse9 tag + * minor fixes to dist creation and spec build + + * add encfsctl man page + + * manpage spelling fixes and install manpage to proper directory + + * make release builds mostly automated + + * add encfs.1 to distribution list so that it will be preprocessed and + include in the distribution so that end-users don't need to have + pod2man installed to get the man page. + + * make man page build conditional on finding pod2man program + + * add encfs man page, clean up encfs usage output + +2004-03-13 Valient Gough + * bump version to 1.0.2 + + * use pkg-config to check for OpenSSL and RLog build settings + + * add support for '--' argument to encfs to pass arbitrary options to + FUSE / FuserMount. + +2004-03-02 Valient Gough + * Bumped version to 1.0.1 for new release. + * TAG: v1.0.1 + + * Fix problem with key generation for Blowfish keys larger then 128 + bits, reported by Jarkko Haapalainen. + + * Fix more configure script problems when --with-extra-includes is + used, reported by Troy Folger. + +2004-02-29 Valient Gough + * use user specified includes during compile tests in configure script + + * update Makefile.am to fix a problem reported by Troy Folger using + --with-extra-includes configure options + +2004-02-27 Valient Gough + * TAG: v1.0 + * yet more minor updates.. + * minor config file fixes. + * update Botan support a little.. + * make startup configuration a bit more user friendly for 1.0 release.. + +2004-02-21 Valient Gough + * fix broken algorithm include from last change + * more cleanup for 1.0 release. + +2004-02-20 Valient Gough + * main.cpp: + fix algorithm selection off-by-1 test + show information when using cipher with fixed size + * test.cpp: + minor change to error message + * main.cpp, FileUtils.cpp: + include unistd.h + * encfsctl.cpp: + fix initializer for struct array + * SSL_Cipher.cpp: + make AES and blowfish support optional based + on configure tests + * Makefile.am: + define _XOPEN_SOURCE and _BSD_SOURCE. + make SSL_Cipher optional based on configure tests + * FileNode.cpp: + make O_LARGEFILE optional + * Config.cpp: + include unistd.h to find read() and write() prototypes + * configure.in: + check for various OpenSSL EVP_* functions + * change to use 32 bit checksum for key storage in SSL_Cipher. + +2004-02-19 Valient Gough + * Remove libencfs shared library. Simplifies installation. + + * add "info" channel for logging of operations which can be activated + with "-v" flag. + + * drop remote changes - they are no longer valid on local branch + + * update admin directory. + * build shared code as libencfs shared library. + * other minor cleanup. + + * close substantial memory leak + +2004-02-18 Valient Gough + * implement password change command in encfsctl. + * fix serious bug in SSL_Cipher::readKey + + * fix test program fatal - needed encfs.h include to pick up fuse.h + + * implement new .encfs4 version 4 control file for new filesystems. It + allows specification of different ciphers, key size and blocksize. + + * Add encfsctl program which can display some information about the + filesystem. + +2004-02-14 Valient Gough + * some configuration cleanup. + * added means of getting list of available ciphers. + +2004-02-13 Valient Gough + * add SSL_Cipher which implements AES, Blowfish, Twofish, and TripleDES + ciphers using OpenSSL. + +2004-02-10 Valient Gough + * cleanup and error checking + +2004-02-09 Valient Gough + * allow user to disable use of Botan library (and SSL library) in + configuration. + + * fix 2GB limit (taken from stable branch). + * use Botan or SSL memory management for temporary buffers. + * change block numbers to 64bit. + +2004-02-07 Valient Gough + * TAG: v0.6 + * fix 2GB limit + + * performance improvement eliminating bug that caused unecessary open() + calls. + +2004-01-20 Valient Gough + * remove dependencies on OpenSSL. Make way for adding more ciphers + from other libraries. + +2004-01-17 Valient Gough + * switch logging to use external rlog library + * add syslog logging in daemon (background) mode + +2003-12-18 Valient Gough + * build with Intel's ICC compiler (v8.0). made some changes to avoid + compiler warnings. + + * Fix bug from last changeset due to refactoring nextName() + + * refactor some code in the DirNode hierarchy + * moved mkdir() interface into DirNode + +2003-12-17 Valient Gough + * fix error checking test on readBlock result. + * some minor cleanup. + +2003-12-14 Valient Gough + * lots of bug fixes and improvements + * development branch nearly stable (more testing and memory leak + checking needed) + +2003-12-13 Valient Gough + * fixes to build after merge from stable branch + + * remove derived files + + * merge from stable to development branch + + * bug fixes for readdir and decipher calls + +2003-12-12 Valient Gough + * TAG: v0.5 + * fixes to build - update to libtool 1.5 + * change bootstrap to use admin/Makefile.common + * change default language for tests to C++ + * add fsync support if support is detected in FUSE + +2003-12-11 Valient Gough + * initial work in modularizing parts of encfs + * compiles, but doesn't work properly yet. + + * Likely memory leak and or file descriptor leak in DirNode / FileNode + handling + +2003-12-07 Valient Gough + * Delete: configure + * Delete: aclocal.m4 + +2003-12-03 Valient Gough + * cleanup mounting when directories do not already exist. Reported by + Frank B. + +2003-11-30 Valient Gough + * cleanup reported problems with 0.4: + - check for absolute pathname problem, reported by Marcin K. + - cleanup of build script problems reported by Duane D. + +2003-11-22 Valient Gough + * change encfs/Makefile.am to not install the stupid test program + +2003-11-21 Valient Gough + * TAG: v0.4 + * remove derived files (Makefile.in , configure) + * add comments in README and in usage from program itself + + * fix daemon mode by turning off all message output. Without this the + program was exiting when it tried to print out a message in daemon + mode.. + +2003-11-04 Valient Gough + * new option parsing code checks all arguments + * added daemon mode where process forks and runs in background + +2003-11-03 Valient Gough + * change license to GPL + * minor change to compile cleanly on SuSE 9.0 + +2003-10-30 Valient Gough + * bump version number to 0.3 + * TAG: v0.3 + + * - add workaround to fix problem with tar creating read-only files + * - fix some error code return results to return the proper errno + +2003-10-22 Valient Gough + * TAG: v0.2 + * regenerated Makefile.in + * documentation updates + * change random key to use SHA1 instead of self-encypher + +2003-10-13 Valient Gough + * add basic memory pooling to avoid repeated calls to malloc/free. + + * bug fix - encfs_release was being treated as a 'close', fixed. + * feature - add mutex for threadsafe access to descriptor map + * cleanup - simplify changeBase2Inline in base64 + * cleanup - remove unecessary headers from some modules + +2003-10-12 Valient Gough + * add comments to give some rough idea of usage + + * make blockSize global property, fix compiler warnings + + * fix symbolic link handling and file reference counting + +2003-10-11 Valient Gough + * implement basic file descriptor caching. not thread safe + +2003-07-13 Valient Gough + * TAG: v0.2 + * fixes so that make dist creates correct file + * removed Qt dependency. Now builds with just stl, openssl, pthread + libs. + * Switched volume key to .encfs3 as filenames are not compatible with + previous version since CRC-16 filename checksum was replaced with a + reduced SHA1 HMAC. + +2003-07-09 Valient Gough + * add usage example to README + * TAG: v0.1 + + * copy over latest changes from local branch - removal of debug + statments to make it less verbose.. + + * fix big memory leak + * add README + + * Initial repository create + diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..06d2fba --- /dev/null +++ b/INSTALL @@ -0,0 +1,23 @@ + + +Encfs uses the GNU autoconf / automake toolchain to create makefiles. + +The configure script is automatically generated, but is part of most EncFS +distributions. If you have a distribution that does not contain the configure +script, then you can generate it by either running the "makeconf.sh" script, or +running "make -f Makefile.dist". + +To build encfs, run: + +./configure +make + +This creates two executables, encfs and encfsctl in the encfs directory. You +can install to in a system directory via "make install". If the default path +(/usr/local) is not where you want things installed, then use the "--prefix" +option to configure to specify the install prefix. + +Encfs and encfsctl can also be installed by hand. They need no special +permissions. You may also want the man pages encfs.1 and encfsctl.1. + + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..4908fb0 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,17 @@ + +if BUILD_NLS +NLS_DIR = po +endif + +SUBDIRS = encfs m4 $(NLS_DIR) + + +EXTRA_DIST = config.rpath mkinstalldirs encfs.spec makedist.sh makedist2.sh \ + intl/autosprintf.h intl/autosprintf.cpp intl/gettext.h + +AUTOMAKE_OPTIONS = foreign + +MAINTAINERCLEANFILES = aclocal.m4 + + +ACLOCAL_AMFLAGS = -I m4 diff --git a/Makefile.common b/Makefile.common new file mode 100644 index 0000000..f6faeb4 --- /dev/null +++ b/Makefile.common @@ -0,0 +1,2 @@ +KDE_OPTIONS = qtonly + diff --git a/Makefile.dist b/Makefile.dist new file mode 100644 index 0000000..d160702 --- /dev/null +++ b/Makefile.dist @@ -0,0 +1,8 @@ +default: all + +all: + aclocal + autoheader + automake + autoconf + diff --git a/README b/README new file mode 100644 index 0000000..7cb63cb --- /dev/null +++ b/README @@ -0,0 +1,132 @@ + +For notes about internationalization, see README-NLS. + +EncFS is a program which provides an encrypted virtual filesystem for Linux +using the FUSE kernel module ( see http://sourceforge.net/projects/avf to +download the latest version of FUSE ). FUSE provides a loadable kernel module +which exports a filesystem interface to user-mode. EncFS runs entirely in +user-mode and acts as a transparent encrypted filesystem. + +Usage: + + - To see command line options, see the man page for encfs and encfsctl, or for + brief usage message, run the programs without an argument (or '-h'): + % encfs -h + % man encfs + + - To create a new encrypted filesystem: + % encfs [source dir] [destination mount point] + + eg.: "encfs ~/.crypt ~/crypt". Both directories should already exist, + although Encfs will ask if it can create them if they do not. If the + "~/.crypt" directory does not already contain encrypted filesystem data, + then the user is prompted for a password for the new encryption directory. + The encrypted files will be stored in ~/.crypt, and plaintext access will be + through ~/crypt + + - To mount an existing filesystem: + % encfs [source dir] [destination mount point] + + This works just like creating a new filesystem. If the Encfs control file + is found in the directory, then an attempt is made to mount an existing + filesystem. If the control file is not found, then the filesystem is + created. + + +Technology: + + - Encfs uses algorithms from third-party libraries (OpenSSL is the default) to + encrypt data and filenames. + + - a user supplied password is used to decrypt a volume key, and the volume key + is used for encrypting all file names and contents. This makes it possible + to change the password without needing to re-encrypt all files. + + - EncFS has two encryption modes, which are used in different places: + - Stream encryption: + Used for filenames and partial blocks at the end of files. + The cipher is run in CFB stream mode in multiple passes over the data, + with data order reversal between passes to make data more + interdependent. + - Block encryption: + Fixed size filesystem blocks are encrypted using the cipher in CBC + mode. The filesystem block size is a multiple of the cipher block + size, and is configurable on filesystem creation and can be up to 4096 + bytes in size. Each block has a deterministic initialization vector + which allows for simple random access to blocks within a file. + + - Filename encryption: + + Filenames are encrypted using either a stream mode or a block mode, in both + cases with an initialization vector based on the HMAC checksum of the + filename. + + Using a deterministic initial vector allows fast directory lookups, as no + salt value needs to be looked up when converting from plaintext name to + encrypted name. It also means very similar filenames (such as "foo1" and + "foo2") will encrypt to very different values, to frustrate any attempt to + see how closely related two files are based on their encrypted names. + + - Data blocks are handled in fixed size blocks (64 byte blocks for Encfs + versions 0.2 - 0.6, and user specified sizes in newer versions of Encfs, + defaulting to 512 byte block). The block size is set during creation of the + filesystem and is constant thereafter. + Full filesystem blocks are encrypted in the cipher's block mode as described + above. Partial filesystem blocks are encrypted using the cipher's stream + mode, which involves multiple passes over the data along with data + reordering to make the data in the partial block highly interdependent. + + For both modes this means that a change to a byte in the encrypted stream + may affecting several bytes in the deciphered stream. This makes it hard + for any change at all to go unnoticed. + + An additional option is to store Message Authentication Codes with each + filesystem block. This adds about 8 bytes of overhead per block and a + large performance penalty, but makes it possible detect any modification + within a block. + + Also during filesystem creation, one can enable per-file initialization + vectors. This causes a header with a random initialization vector to be + maintained with each file. Each file then has its own 64 bit initialization + vector which is augmented by the block number - so that each block within a + file has a unique initialization vector. This makes it infeasible to copy a + whole block from one file to another. + +Backward Compatibility: + + At the top level of the raw (encrypted) storage for an EncFS filesystem is a + configuration file, created automatically by EncFS when a new filesystem is + made. + + In Encfs versions 0.2 to 0.6, the file was called ".encfs3" - meaning + version 3 of the Encfs configuration file format (earlier versions 1 and 2 + were prior to the encfs public release). EncFS 1.0.x used ".encfs4", and + the Encfs 1.1.x uses yet another format (".encfs5"). The encfsctl program + can be used to show information about a filesystem. + + Encfs 1.1 can read and write to existing filesystems, but older versions + will not be able to mount a filesystem created by a newer version, as the + newer versions use algorithms and/or new options which were not previously + available. + +Utility: + + In addition to the "encfs" main program, a utility "encfsctl" has been + provided which can perform some operations on encfs filesystems. Encfsctl + can display information about the filesystem for the curious (the encryption + algorithm used, key length, block size), and more importantly it can also + change the user-supplied password used to encrypt the volume key. + +Dependencies: + + Encfs uses the OpenSSL toolkit (http://www.openssl.org) by default. + OpenSSL is not covered by the GPL, and some people are concerned about the + licenses being incompatible. Although I believe it should be clear that I + intended to allow linking encfs with OpenSSL, I will make it more explicit: + + As a special exception to encfs's GPL license, the copyright holders give + permission to link the code or portions of this program with the OpenSSL + library, and distribute linked combinations including the two. This + exception should be construed as narrowly as possible to allow OpenSSL to be + used and distributed as part of encfs. + diff --git a/README-NLS b/README-NLS new file mode 100644 index 0000000..422156d --- /dev/null +++ b/README-NLS @@ -0,0 +1,71 @@ + +Quick configuration advice +========================== + +The configuration script will automatically find and make use of your installed +'gettext' package. If you do not have gettext installed, or do not want +internationalization support included in the build, then you can disable native +language support using + ./configu --disable-nls + +Using This Package +================== + +As a user, if your language has been installed for this package, you +only have to set the `LANG' environment variable to the appropriate +`LL_CC' combination. Here `LL' is an ISO 639 two-letter language code, +and `CC' is an ISO 3166 two-letter country code. For example, let's +suppose that you speak German and live in Germany. At the shell +prompt, merely execute `setenv LANG de_DE' (in `csh'), +`export LANG; LANG=de_DE' (in `sh') or `export LANG=de_DE' (in `bash'). +This can be done from your `.login' or `.profile' file, once and for +all. + + You might think that the country code specification is redundant. +But in fact, some languages have dialects in different countries. For +example, `de_AT' is used for Austria, and `pt_BR' for Brazil. The +country code serves to distinguish the dialects. + + The locale naming convention of `LL_CC', with `LL' denoting the +language and `CC' denoting the country, is the one use on systems based +on GNU libc. On other systems, some variations of this scheme are +used, such as `LL' or `LL_CC.ENCODING'. You can get the list of +locales supported by your system for your country by running the command +`locale -a | grep '^LL''. + + Not all programs have translations for all languages. By default, an +English message is shown in place of a nonexistent translation. If you +understand other languages, you can set up a priority list of languages. +This is done through a different environment variable, called +`LANGUAGE'. GNU `gettext' gives preference to `LANGUAGE' over `LANG' +for the purpose of message handling, but you still need to have `LANG' +set to the primary language; this is required by other parts of the +system libraries. For example, some Swedish users who would rather +read translations in German than English for when Swedish is not +available, set `LANGUAGE' to `sv:de' while leaving `LANG' to `sv_SE'. + + Special advice for Norwegian users: The language code for Norwegian +bokma*l changed from `no' to `nb' recently (in 2003). During the +transition period, while some message catalogs for this language are +installed under `nb' and some older ones under `no', it's recommended +for Norwegian users to set `LANGUAGE' to `nb:no' so that both newer and +older translations are used. + + In the `LANGUAGE' environment variable, but not in the `LANG' +environment variable, `LL_CC' combinations can be abbreviated as `LL' +to denote the language's main dialect. For example, `de' is equivalent +to `de_DE' (German as spoken in Germany), and `pt' to `pt_PT' +(Portuguese as spoken in Portugal) in this context. + +Translating +=========== + +EncFS is registered with Rosetta - an online interface for supplying +translations. See https://launchpad.ubuntu.com/rosetta/products/encfs + +If your language is not included in this distribution, you may want +to check if translated text is already available online in Rosetta. +If not, consider translating some of the strings, which will then be +included in the next EncFS release. + + diff --git a/TRANSLATORS b/TRANSLATORS new file mode 100644 index 0000000..58e520f --- /dev/null +++ b/TRANSLATORS @@ -0,0 +1,7 @@ +Many people have contributed translations for EncFS. Thank you for making +EncFS easier for everyone to use! + +If you would like to help with translations, please use the online +interface provided by Canonical Ltd (the makers of Ubuntu Linux): + https://translations.launchpad.net/encfs/main/ + diff --git a/acinclude.m4 b/acinclude.m4 new file mode 100644 index 0000000..d8e2a29 --- /dev/null +++ b/acinclude.m4 @@ -0,0 +1,3 @@ + +m4_include([acx_pthread.m4]) + diff --git a/acx_pthread.m4 b/acx_pthread.m4 new file mode 100644 index 0000000..bedf51c --- /dev/null +++ b/acx_pthread.m4 @@ -0,0 +1,199 @@ +dnl Available from the GNU Autoconf Macro Archive at: +dnl http://www.gnu.org/software/ac-archive/htmldoc/acx_pthread.html +dnl +AC_DEFUN([ACX_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +AC_LANG_SAVE +AC_LANG_C +acx_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) + AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes) + AC_MSG_RESULT($acx_pthread_ok) + if test x"$acx_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all, and "pthread-config" +# which is a program returning the flags for the Pth emulation library. + +acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case "${host_cpu}-${host_os}" in + *solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthread or + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + acx_pthread_flags="-pthread -pthreads pthread -mt $acx_pthread_flags" + ;; +esac + +if test x"$acx_pthread_ok" = xno; then +for flag in $acx_pthread_flags; do + + case $flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $flag]) + PTHREAD_CFLAGS="$flag" + ;; + + pthread-config) + AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no) + if test x"$acx_pthread_config" = xno; then continue; fi + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$flag]) + PTHREAD_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + AC_TRY_LINK([#include ], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], + [acx_pthread_ok=yes]) + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + AC_MSG_RESULT($acx_pthread_ok) + if test "x$acx_pthread_ok" = xyes; then + break; + fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$acx_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: threads are created detached by default + # and the JOINABLE attribute has a nonstandard name (UNDETACHED). + AC_MSG_CHECKING([for joinable pthread attribute]) + AC_TRY_LINK([#include ], + [int attr=PTHREAD_CREATE_JOINABLE;], + ok=PTHREAD_CREATE_JOINABLE, ok=unknown) + if test x"$ok" = xunknown; then + AC_TRY_LINK([#include ], + [int attr=PTHREAD_CREATE_UNDETACHED;], + ok=PTHREAD_CREATE_UNDETACHED, ok=unknown) + fi + if test x"$ok" != xPTHREAD_CREATE_JOINABLE; then + AC_DEFINE(PTHREAD_CREATE_JOINABLE, $ok, + [Define to the necessary symbol if this constant + uses a non-standard name on your system.]) + fi + AC_MSG_RESULT(${ok}) + if test x"$ok" = xunknown; then + AC_MSG_WARN([we do not know how to create joinable pthreads]) + fi + + AC_MSG_CHECKING([if more special flags are required for pthreads]) + flag=no + case "${host_cpu}-${host_os}" in + *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; + *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; + esac + AC_MSG_RESULT(${flag}) + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + # More AIX lossage: must compile with cc_r + AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC}) +else + PTHREAD_CC="$CC" +fi + +AC_SUBST(PTHREAD_LIBS) +AC_SUBST(PTHREAD_CFLAGS) +AC_SUBST(PTHREAD_CC) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$acx_pthread_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) + : +else + acx_pthread_ok=no + $2 +fi +AC_LANG_RESTORE +])dnl ACX_PTHREAD diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..d7a4978 --- /dev/null +++ b/configure.ac @@ -0,0 +1,187 @@ +dnl Process this file with autoconf to produce a configure script. + +AC_INIT(encfs/encfs.h) dnl a source file from your sub dir +AM_INIT_AUTOMAKE(encfs, 1.4.0) dnl searches for some needed programs + +dnl without this order in this file, automake will be confused! +dnl +AM_CONFIG_HEADER(config.h) + +dnl This ksh/zsh feature conflicts with `cd blah ; pwd` +unset CDPATH + +AC_LANG_CPLUSPLUS +AC_PROG_CXX + +RELEASE=1 +AC_SUBST(RELEASE) + +dnl almost the same like KDE_SET_PEFIX but the path is /usr/local +dnl +unset CDPATH +dnl make /usr/local the default for the installation +AC_PREFIX_DEFAULT(/usr/local) + +AM_GNU_GETTEXT([external]) +AM_GNU_GETTEXT_VERSION(0.14.1) +AM_MKINSTALLDIRS +dnl AC_LIB_LINKFLAGS([asprintf]) # use internal copy + +dnl create only shared libtool-libraries +dnl These can be overridden by command line arguments +AC_ENABLE_SHARED(yes) +AC_ENABLE_STATIC(no) + +AM_CONDITIONAL( BUILD_STATIC, test "x$enable_static" = "xyes" ) +dnl only build either static or shared, not both.. +if test "x$enable_static" = "xyes"; then + enable_shared=no + AC_DEFINE(BUILD_STATIC, [1], [Building static library]) +fi + +AC_PROG_LIBTOOL + +ACX_PTHREAD + +dnl Need to include any user specified flags in the tests below, as they might +dnl specify required include directories.. +FUSEFLAGS="-D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=26" +CPPFLAGS="$CPPFLAGS $USER_INCLUDES $FUSEFLAGS -D__STDC_FORMAT_MACROS" +CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS $USER_INCLUDES" +LDFLAGS="$LDFLAGS $PTHREAD_LIBS $USER_LDFLAGS" + +if test "$GXX" = "yes"; then + CXXFLAGS="-W -Wall -Wshadow -Wpointer-arith -Wwrite-strings $CXXFLAGS" + dnl CXXFLAGS="$CXXFLAGS -Wformat=2 -Wconversion" +fi + +AC_CHECK_HEADER(fuse.h,, + [AC_MSG_ERROR([ + Can't find fuse.h - add the search path to CPPFLAGS and + rerun configure, eg: + export CPPFLAGS=-I/usr/local/include ])]) +AC_CHECK_LIB(fuse,fuse_new, [FUSE_LIBS="-lfuse"], + [AC_MSG_ERROR([ + Can't find libfuse.a - add the search path to LDFLAGS + and rerun configure, eg: + export LDFLAGS=-L/usr/local/lib ])],) + +AC_CHECK_HEADERS([ulockmgr.h]) +AC_CHECK_LIB(ulockmgr,ulockmgr_op, [FUSE_LIBS="$FUSE_LIBS -lulockmgr"]) +AC_SUBST(FUSE_LIBS) + +# check for a supported FUSE_MAJOR_VERSION. +AC_MSG_CHECKING([For supported FUSE API version]) +AC_RUN_IFELSE([ + AC_LANG_PROGRAM([[#include "fuse.h"]], +[[ + if(FUSE_MAJOR_VERSION == 2 && FUSE_MINOR_VERSION >= 5) + { + return 0; + } else + return -1; +]])], + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no]) + AC_MSG_FAILURE([ +Encfs 1.3 requires FUSE 2.5 or newer. Please check config.log for errors. If +you cannot determine the problem, mail encfs-users@lists.sourceforge.net +and include the config.log file]) + ] +) + +dnl fuse_operations.setxattr was added 2004-03-31 +dnl only enable it if setxattr function is found.. +AC_CHECK_HEADERS([attr/xattr.h sys/xattr.h]) + +dnl Check for valgrind headers.. +AC_CHECK_HEADERS([valgrind/valgrind.h valgrind/memcheck.h]) + +# allow user option of not using ssl.. +AC_ARG_ENABLE(openssl, + AC_HELP_STRING([--disable-openssl], + [disables openssl library usage.]), + with_openssl=$enableval, with_openssl="yes" ) + +# try checking for openssl using +if test "x$with_openssl" = "xyes"; then + # look for openssl using pkg-config first.. + PKG_CHECK_MODULES(OPENSSL, openssl >= 0.9.7, + with_openssl="yes", with_openssl="old-test") + + # If that fails, try checking via old methods - which isn't as robust when + # it comes to extra include paths, etc.. + if test "x$with_openssl" = "xold-test"; then + AC_CHECK_HEADER(openssl/ssl.h, + AC_CHECK_LIB(ssl,SSL_new, + [with_openssl="yes"])) + OPENSSL_LIBS="-lssl" + fi + + # if we have openssl, then examine available interfaces. + if test "x$with_openssl" = "xyes"; then + AC_DEFINE(HAVE_SSL, [1], [Linking with OpenSSL]) + + # add in the libs just for the test.. + oldflags=$CXXFLAGS + oldlibs=$LIBS + CXXFLAGS="$CXXFLAGS $OPENSSL_CFLAGS" + LIBS="$LIBS $OPENSSL_LIBS" + AC_CHECK_FUNCS(EVP_aes_128_cbc EVP_aes_192_cbc EVP_aes_256_cbc, + AC_DEFINE(HAVE_EVP_AES, [1], [Have EVP AES interfaces])) + AC_CHECK_FUNCS(EVP_bf_cbc, + AC_DEFINE(HAVE_EVP_BF, [1], [Have EVP Blowfish interfaces])) + AC_CHECK_FUNCS(EVP_CIPHER_CTX_set_padding, + with_opensslevp=yes, + AC_MSG_WARN([New SSL cipher code only enabled for OpenSSL 0.9.7 or later])) + AC_CHECK_FUNCS(HMAC_Init_ex, + AC_DEFINE(HAVE_HMAC_INIT_EX, [1], [Have HMAC_Init_ex function])) + + CXXFLAGS="$oldflags" + LIBS="$oldlibs" + + AC_SUBST(HAVE_EVP_AES) + AC_SUBST(HAVE_EVP_BF) + AC_SUBST(HAVE_HMAC_INIT_EX) + fi +fi +AM_CONDITIONAL( BUILD_OPENSSL, test "x$with_openssl" = "xyes" ) +AM_CONDITIONAL( BUILD_SSLCIPHER, test "x$with_opensslevp" = "xyes" ) +AC_SUBST(HAVE_SSL) + +if test "x$with_openssl" != "xyes"; then + AC_MSG_ERROR( [Encfs requires OpenSSL]) +fi + +# check for RLOG +PKG_CHECK_MODULES(RLOG, librlog >= 1.3, + with_rlog="yes", + [ # do old-style test instead.. + AC_MSG_WARN([Checking for librlog the hard way]) + AC_CHECK_LIB(rlog, RLogVersion, [RLOG_LIBS="-lrlog"], + [AC_MSG_ERROR([EncFS depends on librlog, by the same author.])]) ]) + +AC_CHECK_HEADER(boost/shared_ptr.hpp,, + [AC_MSG_ERROR([ + Can't find boost/shared_.h - add the boost include dir to CPPFLAGS and + rerun configure, eg: + export CPPFLAGS=-I/usr/local/include ])]) + +# look for pod2man program for building man pages +AC_PATH_PROG(POD2MAN, pod2man, [no]) +AC_PATH_PROG(POD2HTML, pod2html, [no]) +AM_CONDITIONAL( BUILD_MAN, test "x$POD2MAN" != "xno" ) +AM_CONDITIONAL( BUILD_MANHTML, test "x$POD2HTML" != "xno" ) +AM_CONDITIONAL( BUILD_NLS, test "x$USE_NLS" != "xno" ) + + +AC_CONFIG_FILES([Makefile] \ + [encfs/Makefile] \ + [encfs.spec] \ + [makedist2.sh] \ + [m4/Makefile] \ + [po/Makefile.in] \ + [po/Makefile]) + +AC_OUTPUT + diff --git a/dk2ChangeLog b/dk2ChangeLog new file mode 100644 index 0000000..97c72a2 --- /dev/null +++ b/dk2ChangeLog @@ -0,0 +1,32 @@ +#!/usr/bin/perl -w + +use strict; + +open(LOG, "darcs changes|") || die "Can't open 'dk changes': $!"; + +my $lastDate = ""; +while() +{ + if( /^(\w+)\s+(\w+)\s+(\d+)\s+([0-9:]+)\s+(\w+)\s+(\d+)\s+(.*)$/ ) + { + my $date = "$1 $2 $3 $6"; + if($date ne $lastDate) + { + $lastDate = $date; + print "$date $7\n"; + } + } else + { + s/^\s+//; + if(/\w/) + { + print "\t$_"; + } else + { + #print "\n"; + } + } +} + +close(LOG) + diff --git a/encfs.spec.in b/encfs.spec.in new file mode 100644 index 0000000..bab0b81 --- /dev/null +++ b/encfs.spec.in @@ -0,0 +1,207 @@ +Name: encfs +Summary: Encrypted pass-thru filesystem for Linux +Version: @VERSION@ +Release: @RELEASE@ +License: GPL +Group: System/Filesystems +Source: %{name}-%{version}-%{release}.tgz +BuildRoot: %{_tmppath}/build-root-%{name} +Packager: Valient Gough +#Distribution: Suse 9.1 +Prefix: /usr +Url: http://pobox.com/~vgough/encfs +Provides: encfs +Provides: encfsctl +Provides: libencfs.1 + +Requires: rlog >= 1.3 +Requires: openssl +Requires: fuse >= 2.2 + +%description +EncFS implements an encrypted filesystem in userspace using FUSE. FUSE +provides a Linux kernel module which allows virtual filesystems to be written +in userspace. EncFS encrypts all data and filenames in the filesystem and +passes access through to the underlying filesystem. Similar to CFS except that +it does not use NFS. + +%changelog +* Fri Nov 11 2005 Valient Gough +- Release 1.2.5 +- Fix race condition when using newer versions of GCC. Fixes problem reported + by Chris at x.nu. +- add encfssh script, thanks to David Rosenstrauch +* Fri Aug 26 2005 Valient Gough +- Release 1.2.4 +- fix segfault if small invalid filenames were encountered in the encrypted + directory, reported by paulgfx. +- try and detect if user tries to mount the filesystem over the top of the + encrypted directory, problem reported by paulgfx. +- environment variable ENCFS5_CONFIG can be used to override the location of + the .encfs5 configuration file. +- add encfsctl 'export' command, patch from Janne Hellsten + +* Tue Apr 19 2005 Valient Gough +- Release 1.2.1 +- add --public mount option +- add --stdinpass option to read password from stdin for scripting +- import latest rosetta translation updates + +* Thu Feb 10 2005 Valient Gough +- Release 1.2.0 +- Fix bug with MAC headers and files > 2GB, reported by Damian Frank +- Fix bug with external password interface which could result in problems + communicating with external password program. Found by Olivier Dournaux. +- Switch to FUSE 2.2 API -- support for FUSE 1.x has been dropped. +- Add support for inode numbering pass-thru (when used 'use_ino' option to + fuse). This allows encoded filesystem to use the same inode numbers as the + underlying filesystem. + +* Wed Jan 12 2005 Valient Gough +- Release 1.1.11 +- add internationalization support. Thanks to lots of contributors, there are + translations for serveral languages. +- added workaround for libfuse mount failure with FUSE 1.4 +- fix compile failure with FUSE 1.4 + +* Mon Nov 8 2004 Valient Gough +- Release 1.1.10 +- fix problems with recursive rename +- fix incorrect error codes from xattr functions + +* Tue Aug 15 2004 Valient Gough +- Release 1.1.9 +- fix another rename bug (affected filesystems with 'paranoia' configuration) + +* Mon Aug 14 2004 Valient Gough +- Release 1.1.8 +- Improve MAC block header processing. + +* Sat Aug 12 2004 Valient Gough +- Release 1.1.7 +- fix bug in truncate() for unopened files. + +* Mon Aug 9 2004 Valient Gough +- Release 1.1.6 +- fix header IV creation when truncate() used to create files. +- add support for IV chaining to old 0.x filesystem support code (useful for + systems with old OpenSSL, like RedHat 7.x). + +* Tue Jul 22 2004 Valient Gough +- Release 1.1.5 + +* Sat Jul 10 2004 Valient Gough +- Release 1.1.4 +- add external password prompt support. + +* Thu Jun 24 2004 Valient Gough +- Release 1.1.3 + +* Fri May 28 2004 Valient Gough +- Release 1.1.2 +- Fix bug affecting filesystems with small empty directories (like XFS) +- Updates to recursive rename code to undo all changes on failure. +- Fix OpenSSL dependency path inclusion in build. + +* Wed May 19 2004 Valient Gough +- Release 1.1.1 +- Fix MAC header memory size allocation error. +- Add file rename-while-open support needed for Evolution. + +* Thu May 13 2004 Valient Gough +- Second release candidate for version 1.1 +- Add support for block mode filename encryption. +- Add support for per-file initialization vectors. +- Add support for directory IV chaining for per-directory initialization + vectors. +- Add support for per-block MAC headers for file contents. +- Backward compatibility support dropped for filesystems created by version + 0.x. Maintains backward compatible support for versions 1.0.x. + +* Sun Apr 4 2004 Valient Gough +- Release 1.0.5 +- Allow truncate call to extend file (only shrinking was supported) + +* Fri Mar 26 2004 Valient Gough +- Release 1.0.4 +- Large speed improvement. +- Add support for FUSE major version 2 API. + +* Thu Mar 18 2004 Valient Gough +- Release 1.0.3 +- Fix bugs in truncation and padding code. + +* Sat Mar 13 2004 Valient Gough +- Release 1.0.2 +- Use pkg-config to check for OpenSSL and RLog build settings +- Add support for '--' argument to encfs to pass arbitrary options to FUSE / + fusermount. +- Add man pages. + +* Tue Mar 2 2004 Valient Gough +- Release 1.0.1 +- Fix problem with using OpenSSL's EVP_BytesToKey function with variable + key length ciphers like Blowfish, as it would only generate 128 bit keys. +- Some configure script changes to make it possible to use --with-extra-include + configure option to pick up any necessary directories for OpenSSL. + +* Fri Feb 27 2004 Valient Gough +- Release 1.0 +- Added some pre-defined configuration options at startup to make filesystem + creation a bit more user friendly. + +* Mon Feb 23 2004 Valient Gough +- Merge development branch to mainline. Source modularized to make it easier + to support different algorithms. +- Added encfsctl program which can show information about an encrypted + directory and can change the user password used to store the volume key. +- Added support for AES and BlowFish with user specified keys and block sizes + (when building with OpenSSL >= 0.9.7). +- Backward compatible with old format, but new filesystems store configuration + information in a new format which is not readable by old encfs versions. + +* Sat Feb 7 2004 Valient Gough +- Improved performance by fixing cache bug which caused cached data to not be + used as often as it could have been. Random seek performance improved by + 600% according to Bonnie++ benchmark. +- Fixed bugs preventing files larger then 2GB. Limit should now be around + 128GB (untested - I don't have that much drive space). > 2GB also requires + recent version of FUSE module (from Feb 6 or later) and an underlying + filesystem which supports large files. +- Release 0.6 + +%prep +rm -rf $RPM_BUILD_ROOT +mkdir $RPM_BUILD_ROOT + +%setup -q + +%build +CFLAGS="$RPM_OPT_FLAGS" CXXFLAGS="$RPM_OPT_FLAGS" \ +./configure --enable-debug=no --prefix=%{prefix} --mandir=%{_mandir} +make SED=/usr/bin/sed -j 2 + +%install +make DESTDIR=$RPM_BUILD_ROOT install-strip + +cd $RPM_BUILD_ROOT + +find . -type d -fprint $RPM_BUILD_DIR/file.list.%{name}.dirs +find . -type f -fprint $RPM_BUILD_DIR/file.list.%{name}.files.tmp +sed '/\/man\//s/$/.gz/g' $RPM_BUILD_DIR/file.list.%{name}.files.tmp > $RPM_BUILD_DIR/file.list.%{name}.files +find . -type l -fprint $RPM_BUILD_DIR/file.list.%{name}.libs +sed '1,2d;s,^\.,\%attr(-\,root\,root) \%dir ,' $RPM_BUILD_DIR/file.list.%{name}.dirs > $RPM_BUILD_DIR/file.list.%{name} +sed 's,^\.,\%attr(-\,root\,root) ,' $RPM_BUILD_DIR/file.list.%{name}.files >> $RPM_BUILD_DIR/file.list.%{name} +sed 's,^\.,\%attr(-\,root\,root) ,' $RPM_BUILD_DIR/file.list.%{name}.libs >> $RPM_BUILD_DIR/file.list.%{name} + +%clean +case "$RPM_BUILD_ROOT" in build-root-*) rm -rf $RPM_BUILD_ROOT ;; esac +rm -f $RPM_BUILD_DIR/file.list.%{name} +rm -f $RPM_BUILD_DIR/file.list.%{name}.libs +rm -f $RPM_BUILD_DIR/file.list.%{name}.files +rm -f $RPM_BUILD_DIR/file.list.%{name}.files.tmp +rm -f $RPM_BUILD_DIR/file.list.%{name}.dirs + +%files -f ../file.list.%{name} + +%defattr(-,root,root,0755) diff --git a/encfs/BlockFileIO.cpp b/encfs/BlockFileIO.cpp new file mode 100644 index 0000000..9e3ff2f --- /dev/null +++ b/encfs/BlockFileIO.cpp @@ -0,0 +1,431 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + */ + +#include "BlockFileIO.h" + +#include "MemoryPool.h" + +#include +#include + +#include "i18n.h" + +template +inline Type min( Type A, Type B ) +{ + return (B < A) ? B : A; +} + +static void clearCache( IORequest &req, int blockSize ) +{ + memset( req.data, 0, blockSize ); + req.dataLen = 0; +} + +BlockFileIO::BlockFileIO( int dataSize ) + : _blockSize( dataSize ) + , _allowHoles( false ) +{ + rAssert( _blockSize > 1 ); + _cache.data = new unsigned char [ _blockSize ]; +} + +BlockFileIO::~BlockFileIO() +{ + clearCache( _cache, _blockSize ); + delete[] _cache.data; +} + +ssize_t BlockFileIO::cacheReadOneBlock( const IORequest &req ) const +{ + // we can satisfy the request even if _cache.dataLen is too short, because + // we always request a full block during reads.. + if((req.offset == _cache.offset) && (_cache.dataLen != 0)) + { + // satisfy request from cache + int len = req.dataLen; + if(_cache.dataLen < len) + len = _cache.dataLen; + memcpy( req.data, _cache.data, len ); + return len; + } else + { + if(_cache.dataLen > 0) + clearCache( _cache, _blockSize ); + + // cache results of read -- issue reads for full blocks + IORequest tmp; + tmp.offset = req.offset; + tmp.data = _cache.data; + tmp.dataLen = _blockSize; + ssize_t result = readOneBlock( tmp ); + if(result > 0) + { + _cache.offset = req.offset; + _cache.dataLen = result; // the amount we really have + if(result > req.dataLen) + result = req.dataLen; // only as much as requested + memcpy( req.data, _cache.data, result ); + } + return result; + } +} + +bool BlockFileIO::cacheWriteOneBlock( const IORequest &req ) +{ + // cache results of write (before pass-thru, because it may be modified + // in-place) + memcpy( _cache.data, req.data, req.dataLen ); + _cache.offset = req.offset; + _cache.dataLen = req.dataLen; + bool ok = writeOneBlock( req ); + if(!ok) + clearCache( _cache, _blockSize ); + return ok; +} + +void BlockFileIO::allowHoles( bool allow ) +{ + _allowHoles = allow; +} + +ssize_t BlockFileIO::read( const IORequest &req ) const +{ + rAssert( _blockSize != 0 ); + + int partialOffset = req.offset % _blockSize; + off_t blockNum = req.offset / _blockSize; + ssize_t result = 0; + + if(partialOffset == 0 && req.dataLen <= _blockSize) + { + // read completely within a single block -- can be handled as-is by + // readOneBloc(). + return cacheReadOneBlock( req ); + } else + { + size_t size = req.dataLen; + + // if the request is larger then a block, then request each block + // individually + MemBlock mb; // in case we need to allocate a temporary block.. + IORequest blockReq; // for requests we may need to make + blockReq.dataLen = _blockSize; + blockReq.data = NULL; + + unsigned char *out = req.data; + while( size ) + { + blockReq.offset = blockNum * _blockSize; + + // if we're reading a full block, then read directly into the + // result buffer instead of using a temporary + if(partialOffset == 0 && size >= (size_t)_blockSize) + blockReq.data = out; + else + { + if(!mb.data) + mb = MemoryPool::allocate( _blockSize ); + blockReq.data = mb.data; + } + + ssize_t readSize = cacheReadOneBlock( blockReq ); + if(unlikely(readSize <= partialOffset)) + break; // didn't get enough bytes + + int cpySize = min( (size_t)(readSize - partialOffset), size ); + rAssert(cpySize <= readSize); + + // if we read to a temporary buffer, then move the data + if(blockReq.data != out) + memcpy( out, blockReq.data + partialOffset, cpySize ); + + result += cpySize; + size -= cpySize; + out += cpySize; + ++blockNum; + partialOffset = 0; + + if(unlikely(readSize < _blockSize)) + break; + } + + if(mb.data) + MemoryPool::release( mb ); + } + + return result; +} + +bool BlockFileIO::write( const IORequest &req ) +{ + rAssert( _blockSize != 0 ); + + off_t fileSize = getSize(); + + // where write request begins + off_t blockNum = req.offset / _blockSize; + int partialOffset = req.offset % _blockSize; + + // last block of file (for testing write overlaps with file boundary) + off_t lastFileBlock = fileSize / _blockSize; + ssize_t lastBlockSize = fileSize % _blockSize; + + off_t lastNonEmptyBlock = lastFileBlock; + if(lastBlockSize == 0) + --lastNonEmptyBlock; + + if( (req.offset > fileSize) && !_allowHoles ) + { + // extend file first to fill hole with 0's.. + const bool forceWrite = false; + padFile( fileSize, req.offset, forceWrite ); + } + + // check against edge cases where we can just let the base class handle the + // request as-is.. + if(partialOffset == 0 && req.dataLen <= _blockSize) + { + // if writing a full block.. pretty safe.. + if( req.dataLen == _blockSize ) + return cacheWriteOneBlock( req ); + + // if writing a partial block, but at least as much as what is + // already there.. + if(blockNum == lastFileBlock && req.dataLen >= lastBlockSize) + return cacheWriteOneBlock( req ); + } + + // have to merge data with existing block(s).. + MemBlock mb; + + IORequest blockReq; + blockReq.data = NULL; + blockReq.dataLen = _blockSize; + + bool ok = true; + size_t size = req.dataLen; + unsigned char *inPtr = req.data; + while( size ) + { + blockReq.offset = blockNum * _blockSize; + int toCopy = min((size_t)(_blockSize - partialOffset), size); + + // if writing an entire block, or writing a partial block that requires + // no merging with existing data.. + if( (toCopy == _blockSize) + ||(partialOffset == 0 && blockReq.offset + toCopy >= fileSize)) + { + // write directly from buffer + blockReq.data = inPtr; + blockReq.dataLen = toCopy; + } else + { + // need a temporary buffer, since we have to either merge or pad + // the data. + if(!mb.data) + mb = MemoryPool::allocate( _blockSize ); + memset( mb.data, 0, _blockSize ); + blockReq.data = mb.data; + + if(blockNum > lastNonEmptyBlock) + { + // just pad.. + blockReq.dataLen = toCopy + partialOffset; + } else + { + // have to merge with existing block data.. + blockReq.dataLen = _blockSize; + blockReq.dataLen = cacheReadOneBlock( blockReq ); + + // extend data if necessary.. + if( partialOffset + toCopy > blockReq.dataLen ) + blockReq.dataLen = partialOffset + toCopy; + } + // merge in the data to be written.. + memcpy( blockReq.data + partialOffset, inPtr, toCopy ); + } + + // Finally, write the damn thing! + if(!cacheWriteOneBlock( blockReq )) + { + ok = false; + break; + } + + // prepare to start all over with the next block.. + size -= toCopy; + inPtr += toCopy; + ++blockNum; + partialOffset = 0; + } + + if(mb.data) + MemoryPool::release( mb ); + + return ok; +} + +int BlockFileIO::blockSize() const +{ + return _blockSize; +} + +void BlockFileIO::padFile( off_t oldSize, off_t newSize, bool forceWrite ) +{ + off_t oldLastBlock = oldSize / _blockSize; + off_t newLastBlock = newSize / _blockSize; + int newBlockSize = newSize % _blockSize; + + IORequest req; + MemBlock mb; + + if(oldLastBlock == newLastBlock) + { + // when the real write occurs, it will have to read in the existing + // data and pad it anyway, so we won't do it here (unless we're + // forced). + if( forceWrite ) + { + mb = MemoryPool::allocate( _blockSize ); + req.data = mb.data; + + req.offset = oldLastBlock * _blockSize; + req.dataLen = oldSize % _blockSize; + int outSize = newSize % _blockSize; // outSize > req.dataLen + + if(outSize) + { + memset( mb.data, 0, outSize ); + cacheReadOneBlock( req ); + req.dataLen = outSize; + cacheWriteOneBlock( req ); + } + } else + rDebug("optimization: not padding last block"); + } else + { + mb = MemoryPool::allocate( _blockSize ); + req.data = mb.data; + + // 1. extend the first block to full length + // 2. write the middle empty blocks + // 3. write the last block + + req.offset = oldLastBlock * _blockSize; + req.dataLen = oldSize % _blockSize; + + // 1. req.dataLen == 0, iff oldSize was already a multiple of blocksize + if(req.dataLen != 0) + { + rDebug("padding block %" PRIi64, oldLastBlock); + memset( mb.data, 0, _blockSize ); + cacheReadOneBlock( req ); + req.dataLen = _blockSize; // expand to full block size + cacheWriteOneBlock( req ); + ++oldLastBlock; + } + + // 2 + for(; oldLastBlock != newLastBlock; ++oldLastBlock) + { + rDebug("padding block %" PRIi64, oldLastBlock); + req.offset = oldLastBlock * _blockSize; + req.dataLen = _blockSize; + memset( mb.data, 0, req.dataLen ); + cacheWriteOneBlock( req ); + } + + // 3. only necessary if write is forced and block is non 0 length + if(forceWrite && newBlockSize) + { + req.offset = newLastBlock * _blockSize; + req.dataLen = newBlockSize; + memset( mb.data, 0, req.dataLen ); + cacheWriteOneBlock( req ); + } + } + + if(mb.data) + MemoryPool::release( mb ); +} + +int BlockFileIO::truncate( off_t size, FileIO *base ) +{ + int partialBlock = size % _blockSize; + int res = 0; + + off_t oldSize = getSize(); + + if( size > oldSize ) + { + // truncate can be used to extend a file as well. truncate man page + // states that it will pad with 0's. + // do the truncate so that the underlying filesystem can allocate + // the space, and then we'll fill it in padFile.. + if(base) + base->truncate( size ); + + const bool forceWrite = true; + padFile( oldSize, size, forceWrite ); + } else + if( size == oldSize ) + { + // the easiest case, but least likely.... + } else + if( partialBlock ) + { + // partial block after truncate. Need to read in the block being + // truncated before the truncate. Then write it back out afterwards, + // since the encoding will change.. + off_t blockNum = size / _blockSize; + MemBlock mb = MemoryPool::allocate( _blockSize ); + + IORequest req; + req.offset = blockNum * _blockSize; + req.dataLen = _blockSize; + req.data = mb.data; + + ssize_t rdSz = cacheReadOneBlock( req ); + + // do the truncate + if(base) + res = base->truncate( size ); + + // write back out partial block + req.dataLen = partialBlock; + bool wrRes = cacheWriteOneBlock( req ); + + if((rdSz < 0) || (!wrRes)) + { + // rwarning - unlikely to ever occur.. + rWarning(_("truncate failure: read %i bytes, partial block of %i"), + (int)rdSz, partialBlock ); + } + + MemoryPool::release( mb ); + } else + { + // truncating on a block bounday. No need to re-encode the last + // block.. + if(base) + res = base->truncate( size ); + } + + return res; +} + diff --git a/encfs/BlockFileIO.h b/encfs/BlockFileIO.h new file mode 100644 index 0000000..20e804a --- /dev/null +++ b/encfs/BlockFileIO.h @@ -0,0 +1,69 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + */ + +#ifndef _BlockFileIO_incl_ +#define _BlockFileIO_incl_ + +#include "FileIO.h" + +/* + Implements block scatter / gather interface. Requires derived classes to + implement readOneBlock() / writeOneBlock() at a minimum. + + When a partial block write is requested it will be turned into a read of + the existing block, merge with the write request, and a write of the full + block. +*/ +class BlockFileIO : public FileIO +{ +public: + BlockFileIO(int blockDataSize); + virtual ~BlockFileIO(); + + // implemented in terms of blocks. + virtual ssize_t read( const IORequest &req ) const; + virtual bool write( const IORequest &req ); + + virtual int blockSize() const; + +protected: + + // default is false, but setting this to true will allow holes to be stored + // in the file. Only works if supported by the underlying FileIO + // implementation.. + void allowHoles( bool allow ); + + int truncate( off_t size, FileIO *base ); + void padFile( off_t oldSize, off_t newSize, bool forceWrite ); + + // same as read(), except that the request.offset field is guarenteed to be + // block aligned, and the request size will not be larger then 1 block. + virtual ssize_t readOneBlock( const IORequest &req ) const =0; + virtual bool writeOneBlock( const IORequest &req ) =0; + + ssize_t cacheReadOneBlock( const IORequest &req ) const; + bool cacheWriteOneBlock( const IORequest &req ); + + int _blockSize; + bool _allowHoles; + + // cache last block for speed... + mutable IORequest _cache; +}; + +#endif + diff --git a/encfs/BlockNameIO.cpp b/encfs/BlockNameIO.cpp new file mode 100644 index 0000000..bc10abc --- /dev/null +++ b/encfs/BlockNameIO.cpp @@ -0,0 +1,213 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + */ + +#include "BlockNameIO.h" + +#include "Cipher.h" +#include "base64.h" + +#include +#include +#include +#include + +#include "i18n.h" + +using namespace rlog; +using namespace rel; +using namespace boost; + +static RLogChannel * Info = DEF_CHANNEL( "info/nameio", Log_Info ); + + +static shared_ptr NewBlockNameIO( const Interface &iface, + const shared_ptr &cipher, const CipherKey &key ) +{ + int blockSize = 8; + if(cipher) + blockSize = cipher->cipherBlockSize(); + + return shared_ptr( new BlockNameIO( iface, cipher, key, blockSize)); +} + +static bool BlockIO_registered = NameIO::Register("Block", + // description of block name encoding algorithm.. + // xgroup(setup) + gettext_noop("Block encoding, hides file name size somewhat"), + BlockNameIO::CurrentInterface(), + NewBlockNameIO); + +/* + - Version 1.0 computed MAC over the filename, but not the padding bytes. + This version was from pre-release 1.1, never publically released, so no + backward compatibility necessary. + + - Version 2.0 includes padding bytes in MAC computation. This way the MAC + computation uses the same number of bytes regardless of the number of + padding bytes. + + - Version 3.0 uses full 64 bit initialization vector during IV chaining. + Prior versions used only the output from the MAC_16 call, giving a 1 in + 2^16 chance of the same name being produced. Using the full 64 bit IV + changes that to a 1 in 2^64 chance.. +*/ +Interface BlockNameIO::CurrentInterface() +{ + // implement major version 3 and 2 + return Interface("nameio/block", 3, 0, 1); +} + +BlockNameIO::BlockNameIO( const rel::Interface &iface, + const shared_ptr &cipher, + const CipherKey &key, int blockSize ) + : _interface( iface.current() ) + , _bs( blockSize ) + , _cipher( cipher ) + , _key( key ) +{ + // just to be safe.. + rAssert( blockSize < 128 ); +} + +BlockNameIO::~BlockNameIO() +{ +} + +Interface BlockNameIO::interface() const +{ + return CurrentInterface(); +} + +int BlockNameIO::maxEncodedNameLen( int plaintextNameLen ) const +{ + // number of blocks, rounded up.. Only an estimate at this point, err on + // the size of too much space rather then too little. + int numBlocks = ( plaintextNameLen + _bs ) / _bs; + int encodedNameLen = numBlocks * _bs + 2; // 2 checksum bytes + return B256ToB64Bytes( encodedNameLen ); +} + +int BlockNameIO::maxDecodedNameLen( int encodedNameLen ) const +{ + int decLen256 = B64ToB256Bytes( encodedNameLen ); + return decLen256 - 2; // 2 checksum bytes removed.. +} + +int BlockNameIO::encodeName( const char *plaintextName, int length, + uint64_t *iv, char *encodedName ) const +{ + // copy the data into the encoding buffer.. + memcpy( encodedName+2, plaintextName, length ); + + // Pad encryption buffer to block boundary.. + int padding = _bs - length % _bs; + if(padding == 0) + padding = _bs; // padding a full extra block! + + memset( encodedName+length+2, (unsigned char)padding, padding ); + + // store the IV before it is modified by the MAC call. + uint64_t tmpIV = 0; + if( iv && _interface >= 3 ) + tmpIV = *iv; + + // include padding in MAC computation + unsigned int mac = _cipher->MAC_16( (unsigned char *)encodedName+2, + length+padding, _key, iv ); + + // add checksum bytes + encodedName[0] = (mac >> 8) & 0xff; + encodedName[1] = (mac ) & 0xff; + + _cipher->blockEncode( (unsigned char *)encodedName+2, length+padding, + (uint64_t)mac ^ tmpIV, _key); + + // convert to base 64 ascii + int encodedStreamLen = length + 2 + padding; + int encLen64 = B256ToB64Bytes( encodedStreamLen ); + + changeBase2Inline( (unsigned char *)encodedName, encodedStreamLen, + 8, 6, true ); + B64ToAscii( (unsigned char *)encodedName, encLen64 ); + + return encLen64; +} + +int BlockNameIO::decodeName( const char *encodedName, int length, + uint64_t *iv, char *plaintextName ) const +{ + int decLen256 = B64ToB256Bytes( length ); + int decodedStreamLen = decLen256 - 2; + + // don't bother trying to decode files which are too small + if(decodedStreamLen < _bs) + throw ERROR("Filename too small to decode"); + + BUFFER_INIT( tmpBuf, 32, (unsigned int)length ); + + // decode into tmpBuf, + AsciiToB64((unsigned char *)tmpBuf, (unsigned char *)encodedName, length); + changeBase2Inline((unsigned char *)tmpBuf, length, 6, 8, false); + + // pull out the header information + unsigned int mac = ((unsigned int)((unsigned char)tmpBuf[0])) << 8 + | ((unsigned int)((unsigned char)tmpBuf[1])); + + uint64_t tmpIV = 0; + if( iv && _interface >= 3 ) + tmpIV = *iv; + + _cipher->blockDecode( (unsigned char *)tmpBuf+2, decodedStreamLen, + (uint64_t)mac ^ tmpIV, _key); + + // find out true string length + int padding = (unsigned char)tmpBuf[2+decodedStreamLen-1]; + int finalSize = decodedStreamLen - padding; + + // might happen if there is an error decoding.. + if(padding > _bs || finalSize < 0) + { + rDebug("padding, _bx, finalSize = %i, %i, %i", padding, + _bs, finalSize); + throw ERROR( "invalid padding size" ); + } + + // copy out the result.. + memcpy(plaintextName, tmpBuf+2, finalSize); + plaintextName[finalSize] = '\0'; + + // check the mac + unsigned int mac2 = _cipher->MAC_16((const unsigned char *)tmpBuf+2, + decodedStreamLen, _key, iv); + + BUFFER_RESET( tmpBuf ); + + if(mac2 != mac) + { + rDebug("checksum mismatch: expected %u, got %u", mac, mac2); + rDebug("on decode of %i bytes", finalSize); + throw ERROR( "checksum mismatch in filename decode" ); + } + + return finalSize; +} + +bool BlockNameIO::Enabled() +{ + return true; +} + diff --git a/encfs/BlockNameIO.h b/encfs/BlockNameIO.h new file mode 100644 index 0000000..bd9c07f --- /dev/null +++ b/encfs/BlockNameIO.h @@ -0,0 +1,65 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + */ + +#ifndef _BlockNameIO_incl_ +#define _BlockNameIO_incl_ + +#include "NameIO.h" +#include "CipherKey.h" + +#include + +class Cipher; + +/* + Implement NameIO interface for filename encoding. Uses cipher in block + mode to encode filenames. The filenames are padded to be a multiple of the + cipher block size. +*/ +class BlockNameIO : public NameIO +{ +public: + static rel::Interface CurrentInterface(); + + BlockNameIO( const rel::Interface &iface, + const boost::shared_ptr &cipher, + const CipherKey &key, int blockSize ); + virtual ~BlockNameIO(); + + virtual rel::Interface interface() const; + + virtual int maxEncodedNameLen( int plaintextNameLen ) const; + virtual int maxDecodedNameLen( int encodedNameLen ) const; + + // hack to help with static builds + static bool Enabled(); +protected: + virtual int encodeName( const char *plaintextName, int length, + uint64_t *iv, char *encodedName ) const; + virtual int decodeName( const char *encodedName, int length, + uint64_t *iv, char *plaintextName ) const; + +private: + int _interface; + int _bs; + boost::shared_ptr _cipher; + CipherKey _key; +}; + + +#endif + diff --git a/encfs/Cipher.cpp b/encfs/Cipher.cpp new file mode 100644 index 0000000..379f1d4 --- /dev/null +++ b/encfs/Cipher.cpp @@ -0,0 +1,208 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2002-2004, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + * + */ + +#include "config.h" + +#include "Cipher.h" +#include "Interface.h" +#include "Range.h" + +#include +#include +#include +#include + +// for static build. Need to reference the modules which are registered at +// run-time, to ensure that the linker doesn't optimize them away. +#include "NullCipher.h" +#include "SSL_Cipher.h" + +using namespace std; +using namespace rel; +using boost::shared_ptr; + +#define REF_MODULE(TYPE) \ + if( !TYPE::Enabled() ) \ + cerr << "referenceModule: should never happen\n"; + +static +void AddSymbolReferences() +{ + REF_MODULE(SSL_Cipher) + REF_MODULE(NullCipher) +} + + +struct CipherAlg +{ + bool hidden; + Cipher::CipherConstructor constructor; + string description; + Interface iface; + Range keyLength; + Range blockSize; +}; + +typedef multimap< string, CipherAlg> CipherMap_t; +static CipherMap_t *gCipherMap = NULL; + +std::list +Cipher::GetAlgorithmList( bool includeHidden ) +{ + AddSymbolReferences(); + + list result; + + if(!gCipherMap) + return result; + + CipherMap_t::const_iterator it; + CipherMap_t::const_iterator mapEnd = gCipherMap->end(); + for(it = gCipherMap->begin(); it != mapEnd; ++it) + { + if(includeHidden || !it->second.hidden) + { + CipherAlgorithm tmp; + tmp.name = it->first; + tmp.description = it->second.description; + tmp.iface = it->second.iface; + tmp.keyLength = it->second.keyLength; + tmp.blockSize = it->second.blockSize; + + result.push_back( tmp ); + } + } + + return result; +} + +bool Cipher::Register(const char *name, const char *description, + const Interface &iface, CipherConstructor fn, bool hidden) +{ + Range keyLength(-1,-1,1); + Range blockSize(-1,-1,1); + return Cipher::Register( name, description, iface, + keyLength, blockSize, fn, hidden ); +} + +bool Cipher::Register(const char *name, const char *description, + const Interface &iface, const Range &keyLength, + const Range &blockSize, + CipherConstructor fn, bool hidden) +{ + if(!gCipherMap) + gCipherMap = new CipherMap_t; + + CipherAlg ca; + ca.hidden = hidden; + ca.constructor = fn; + ca.description = description; + ca.iface = iface; + ca.keyLength = keyLength; + ca.blockSize = blockSize; + + gCipherMap->insert( make_pair(string(name), ca) ); + return true; +} + +shared_ptr Cipher::New(const string &name, int keyLen) +{ + shared_ptr result; + + if(gCipherMap) + { + CipherMap_t::const_iterator it = gCipherMap->find( name ); + if(it != gCipherMap->end()) + { + CipherConstructor fn = it->second.constructor; + // use current interface.. + result = (*fn)( it->second.iface, keyLen ); + } + } + + return result; +} + +shared_ptr Cipher::New( const Interface &iface, int keyLen ) +{ + shared_ptr result; + if(gCipherMap) + { + CipherMap_t::const_iterator it; + CipherMap_t::const_iterator mapEnd = gCipherMap->end(); + + for(it = gCipherMap->begin(); it != mapEnd; ++it) + { + // TODO: we should look for the newest implementation.. + if( it->second.iface.implements( iface ) ) + { + CipherConstructor fn = it->second.constructor; + // pass in requested interface.. + result = (*fn)( iface, keyLen ); + + // if we're not going to compare the options, then just stop + // now.. + break; + } + } + } + + return result; +} + +Cipher::Cipher() +{ +} + +Cipher::~Cipher() +{ +} + +unsigned int Cipher::MAC_32( const unsigned char *src, int len, + const CipherKey &key, uint64_t *chainedIV ) const +{ + uint64_t mac64 = MAC_64( src, len, key, chainedIV ); + + unsigned int mac32 = ((mac64 >> 32) & 0xffffffff) ^ (mac64 & 0xffffffff); + + return mac32; +} + +unsigned int Cipher::MAC_16( const unsigned char *src, int len, + const CipherKey &key, uint64_t *chainedIV ) const +{ + uint64_t mac64 = MAC_64( src, len, key, chainedIV ); + + unsigned int mac32 = ((mac64 >> 32) & 0xffffffff) ^ (mac64 & 0xffffffff); + unsigned int mac16 = ((mac32 >> 16) & 0xffff) ^ (mac32 & 0xffff); + + return mac16; +} + +bool Cipher::nameEncode( unsigned char *data, int len, + uint64_t iv64, const CipherKey &key ) const +{ + return streamEncode( data, len, iv64, key ); +} + +bool Cipher::nameDecode( unsigned char *data, int len, + uint64_t iv64, const CipherKey &key ) const +{ + return streamDecode( data, len, iv64, key ); +} + diff --git a/encfs/Cipher.h b/encfs/Cipher.h new file mode 100644 index 0000000..c585688 --- /dev/null +++ b/encfs/Cipher.h @@ -0,0 +1,151 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2002-2003, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + * + */ + +#ifndef _Cipher_incl_ +#define _Cipher_incl_ + +#include "encfs.h" + +#include "Range.h" +#include "Interface.h" +#include "CipherKey.h" + +#include +#include +#include + +using boost::shared_ptr; + +/* + Mostly pure virtual interface defining operations on a cipher. + + Cipher's should register themselves so they can be instanciated via + Cipher::New(). +*/ +class Cipher +{ +public: + // if no key length was indicated when cipher was registered, then keyLen + // <= 0 will be used. + typedef boost::shared_ptr (*CipherConstructor)( const rel::Interface &iface, + int keyLenBits ); + + struct CipherAlgorithm + { + std::string name; + std::string description; + rel::Interface iface; + Range keyLength; + Range blockSize; + }; + + + typedef std::list AlgorithmList; + static AlgorithmList GetAlgorithmList( bool includeHidden = false ); + + + static boost::shared_ptr New( const rel::Interface &iface, + int keyLen = -1); + static boost::shared_ptr New( const std::string &cipherName, + int keyLen = -1 ); + + + static bool Register(const char *cipherName, + const char *description, + const rel::Interface &iface, + CipherConstructor constructor, + bool hidden = false); + static bool Register(const char *cipherName, + const char *description, + const rel::Interface &iface, + const Range &keyLength, const Range &blockSize, + CipherConstructor constructor, + bool hidden = false); + + + Cipher(); + virtual ~Cipher(); + + virtual rel::Interface interface() const =0; + + // create a new key based on a password + virtual CipherKey newKey(const char *password, int passwdLength) =0; + // create a new random key + virtual CipherKey newRandomKey() =0; + + // data must be len encodedKeySize() + virtual CipherKey readKey(const unsigned char *data, + const CipherKey &encodingKey, + bool checkKey = true) =0; + virtual void writeKey(const CipherKey &key, unsigned char *data, + const CipherKey &encodingKey) =0; + // for testing purposes + virtual bool compareKey( const CipherKey &A, const CipherKey &B ) const =0; + + // meta-data about the cypher + virtual int keySize() const=0; + virtual int encodedKeySize() const=0; // size + virtual int cipherBlockSize() const=0; // size of a cipher block + + // fill the supplied buffer with random data + // The data may be pseudo random and might not be suitable for key + // generation. For generating keys, uses newRandomKey() instead. + virtual void randomize( unsigned char *buf, int len ) const =0; + + // 64 bit MAC of the data with the given key + virtual uint64_t MAC_64( const unsigned char *src, int len, + const CipherKey &key, uint64_t *chainedIV = 0 ) const =0; + // based on reductions of MAC_64 + unsigned int MAC_32( const unsigned char *src, int len, + const CipherKey &key, uint64_t *chainedIV = 0 ) const; + unsigned int MAC_16( const unsigned char *src, int len, + const CipherKey &key, uint64_t *chainedIV = 0 ) const; + + // functional interfaces + /* + Stream encoding of data in-place. The stream data can be any length. + */ + virtual bool streamEncode( unsigned char *data, int len, + uint64_t iv64, const CipherKey &key) const=0; + virtual bool streamDecode( unsigned char *data, int len, + uint64_t iv64, const CipherKey &key) const=0; + + /* + These are just aliases of streamEncode / streamDecode, but there are + provided here for backward compatibility for earlier ciphers that has + effectively two stream modes - one for encoding partial blocks and + another for encoding filenames. + */ + virtual bool nameEncode( unsigned char *data, int len, + uint64_t iv64, const CipherKey &key) const; + virtual bool nameDecode( unsigned char *data, int len, + uint64_t iv64, const CipherKey &key) const; + + /* + Block encoding of data in-place. The data size should be a multiple of + the cipher block size. + */ + virtual bool blockEncode(unsigned char *buf, int size, + uint64_t iv64, const CipherKey &key) const=0; + virtual bool blockDecode(unsigned char *buf, int size, + uint64_t iv64, const CipherKey &key) const=0; +}; + + +#endif + diff --git a/encfs/CipherFileIO.cpp b/encfs/CipherFileIO.cpp new file mode 100644 index 0000000..8b02518 --- /dev/null +++ b/encfs/CipherFileIO.cpp @@ -0,0 +1,424 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + */ + +#include "CipherFileIO.h" + +#include "Cipher.h" +#include "MemoryPool.h" + +#include + +#include +#include + +using boost::shared_ptr; + +/* + - Version 2:0 adds support for a per-file initialization vector with a + fixed 8 byte header. The headers are enabled globally within a + filesystem at the filesystem configuration level. + When headers are disabled, 2:0 is compatible with version 1:0. +*/ +static rel::Interface CipherFileIO_iface("FileIO/Cipher", 2, 0, 1); + +const int HEADER_SIZE = 8; // 64 bit initialization vector.. + +static bool checkSize( int fsBlockSize, int cipherBlockSize ) +{ + int blockBoundary = fsBlockSize % cipherBlockSize ; + if(blockBoundary != 0) + { + rError("CipherFileIO: blocks should be multiple of cipher block size"); + return true; + } else + return false; +} + +CipherFileIO::CipherFileIO( const shared_ptr &_base, + const shared_ptr &_cipher, + const CipherKey &_key, int fsBlockSize, + bool uniqueIV, bool _reverseEncryption ) + : BlockFileIO( fsBlockSize ) + , base( _base ) + , cipher( _cipher ) + , key( _key ) + , haveHeader( uniqueIV ) + , externalIV( 0 ) + , fileIV( 0 ) + , lastFlags( 0 ) + , reverseEncryption( _reverseEncryption ) +{ + static bool warnOnce = false; + + if(!warnOnce) + warnOnce = checkSize( fsBlockSize, cipher->cipherBlockSize() ); +} + +CipherFileIO::~CipherFileIO() +{ +} + +rel::Interface CipherFileIO::interface() const +{ + return CipherFileIO_iface; +} + +int CipherFileIO::open( int flags ) +{ + int res = base->open( flags ); + + if( res >= 0 ) + lastFlags = flags; + + return res; +} + +void CipherFileIO::setFileName( const char *fileName ) +{ + base->setFileName( fileName ); +} + +const char *CipherFileIO::getFileName() const +{ + return base->getFileName(); +} + +bool CipherFileIO::setIV( uint64_t iv ) +{ + rDebug("in setIV, current IV = %" PRIu64 ", new IV = %" PRIu64 + ", fileIV = %" PRIu64, + externalIV, iv, fileIV); + if(externalIV == 0) + { + // we're just being told about which IV to use. since we haven't + // initialized the fileIV, there is no need to just yet.. + externalIV = iv; + if(fileIV != 0) + rWarning("fileIV initialized before externalIV! (%" PRIu64 + ", %" PRIu64 ")", fileIV, externalIV); + } else + if(haveHeader) + { + // we have an old IV, and now a new IV, so we need to update the fileIV + // on disk. + if(fileIV == 0) + { + // ensure the file is open for read/write.. + int newFlags = lastFlags | O_RDWR; + int res = base->open( newFlags ); + if(res < 0) + { + if(res == -EISDIR) + { + // duh -- there are no file headers for directories! + externalIV = iv; + return base->setIV( iv ); + } else + { + rDebug("writeHeader failed to re-open for write"); + return false; + } + } + initHeader(); + } + + uint64_t oldIV = externalIV; + externalIV = iv; + if(!writeHeader()) + { + externalIV = oldIV; + return false; + } + } + + return base->setIV( iv ); +} + +int CipherFileIO::getAttr( struct stat *stbuf ) const +{ + int res = base->getAttr( stbuf ); + // adjust size if we have a file header + if((res == 0) && haveHeader && + S_ISREG(stbuf->st_mode) && (stbuf->st_size > 0)) + { + rAssert(stbuf->st_size >= HEADER_SIZE); + stbuf->st_size -= HEADER_SIZE; + } + + return res; +} + +off_t CipherFileIO::getSize() const +{ + off_t size = base->getSize(); + // No check on S_ISREG here -- don't call getSize over getAttr unless this + // is a normal file! + if(haveHeader && size > 0) + { + rAssert(size >= HEADER_SIZE); + size -= HEADER_SIZE; + } + return size; +} + +void CipherFileIO::initHeader( ) +{ + // check if the file has a header, and read it if it does.. Otherwise, + // create one. + off_t rawSize = base->getSize(); + if(rawSize >= HEADER_SIZE) + { + rDebug("reading existing header, rawSize = %" PRIi64, rawSize); + // has a header.. read it + unsigned char buf[8] = {0}; + + IORequest req; + req.offset = 0; + req.data = buf; + req.dataLen = 8; + base->read( req ); + + cipher->streamDecode( buf, sizeof(buf), externalIV, key ); + + fileIV = 0; + for(int i=0; i<8; ++i) + fileIV = (fileIV << 8) | (uint64_t)buf[i]; + + rAssert(fileIV != 0); // 0 is never used.. + } else + { + rDebug("creating new file IV header"); + + unsigned char buf[8] = {0}; + do + { + cipher->randomize( buf, 8 ); + + fileIV = 0; + for(int i=0; i<8; ++i) + fileIV = (fileIV << 8) | (uint64_t)buf[i]; + + if(fileIV == 0) + rWarning("Unexpected result: randomize returned 8 null bytes!"); + } while(fileIV == 0); // don't accept 0 as an option.. + + if( base->isWritable() ) + { + cipher->streamEncode( buf, sizeof(buf), externalIV, key ); + + IORequest req; + req.offset = 0; + req.data = buf; + req.dataLen = 8; + + base->write( req ); + } else + rDebug("base not writable, IV not written.."); + } + rDebug("initHeader finished, fileIV = %" PRIu64 , fileIV); +} + +bool CipherFileIO::writeHeader( ) +{ + if( !base->isWritable() ) + { + // open for write.. + int newFlags = lastFlags | O_RDWR; + if( base->open( newFlags ) < 0 ) + { + rDebug("writeHeader failed to re-open for write"); + return false; + } + } + + if(fileIV == 0) + rError("Internal error: fileIV == 0 in writeHeader!!!"); + rDebug("writing fileIV %" PRIu64 , fileIV); + + unsigned char buf[8] = {0}; + for(int i=0; i<8; ++i) + { + buf[sizeof(buf)-1-i] = (unsigned char)(fileIV & 0xff); + fileIV >>= 8; + } + + cipher->streamEncode( buf, sizeof(buf), externalIV, key ); + + IORequest req; + req.offset = 0; + req.data = buf; + req.dataLen = 8; + + base->write( req ); + + return true; +} + +ssize_t CipherFileIO::readOneBlock( const IORequest &req ) const +{ + // read raw data, then decipher it.. + int bs = blockSize(); + off_t blockNum = req.offset / bs; + + ssize_t readSize = 0; + if(haveHeader) + { + IORequest tmpReq = req; + tmpReq.offset += HEADER_SIZE; + readSize = base->read( tmpReq ); + } else + readSize = base->read( req ); + + bool ok; + if(readSize > 0) + { + if(haveHeader && fileIV == 0) + const_cast(this)->initHeader(); + + if(readSize != bs) + { + ok = streamRead( req.data, (int)readSize, blockNum ^ fileIV); + } else + { + ok = blockRead( req.data, (int)readSize, blockNum ^ fileIV); + } + + if(!ok) + { + rDebug("decodeBlock failed for block %" PRIi64 ", size %i", + blockNum, (int)readSize ); + readSize = -1; + } + } else + rDebug("readSize zero for offset %" PRIi64, req.offset); + + return readSize; +} + + +bool CipherFileIO::writeOneBlock( const IORequest &req ) +{ + int bs = blockSize(); + off_t blockNum = req.offset / bs; + + if(haveHeader && fileIV == 0) + initHeader(); + + bool ok; + if( req.dataLen != bs ) + { + ok = streamWrite( req.data, (int)req.dataLen, + blockNum ^ fileIV ); + } else + { + ok = blockWrite( req.data, (int)req.dataLen, + blockNum ^ fileIV ); + } + + if( ok ) + { + if(haveHeader) + { + IORequest tmpReq = req; + tmpReq.offset += HEADER_SIZE; + ok = base->write( tmpReq ); + } else + ok = base->write( req ); + } else + { + rDebug("encodeBlock failed for block %" PRIi64 ", size %i", + blockNum, req.dataLen); + ok = false; + } + return ok; +} + +bool CipherFileIO::blockWrite( unsigned char *buf, int size, + uint64_t _iv64 ) const +{ + if (!reverseEncryption) + return cipher->blockEncode( buf, size, _iv64, key ); + else + return cipher->blockDecode( buf, size, _iv64, key ); +} + +bool CipherFileIO::streamWrite( unsigned char *buf, int size, + uint64_t _iv64 ) const +{ + if (!reverseEncryption) + return cipher->streamEncode( buf, size, _iv64, key ); + else + return cipher->streamDecode( buf, size, _iv64, key ); +} + + +bool CipherFileIO::blockRead( unsigned char *buf, int size, + uint64_t _iv64 ) const +{ + if (reverseEncryption) + return cipher->blockEncode( buf, size, _iv64, key ); + else + return cipher->blockDecode( buf, size, _iv64, key ); +} + +bool CipherFileIO::streamRead( unsigned char *buf, int size, + uint64_t _iv64 ) const +{ + if (reverseEncryption) + return cipher->streamEncode( buf, size, _iv64, key ); + else + return cipher->streamDecode( buf, size, _iv64, key ); +} + + + +int CipherFileIO::truncate( off_t size ) +{ + int res = 0; + if(!haveHeader) + { + res = BlockFileIO::truncate( size, base.get() ); + } else + { + if(0 == fileIV) + { + // empty file.. create the header.. + if( !base->isWritable() ) + { + // open for write.. + int newFlags = lastFlags | O_RDWR; + if( base->open( newFlags ) < 0 ) + rDebug("writeHeader failed to re-open for write"); + } + initHeader(); + } + + // can't let BlockFileIO call base->truncate(), since it would be using + // the wrong size.. + res = BlockFileIO::truncate( size, 0 ); + + if(res == 0) + base->truncate( size + HEADER_SIZE ); + } + return res; +} + +bool CipherFileIO::isWritable() const +{ + return base->isWritable(); +} + diff --git a/encfs/CipherFileIO.h b/encfs/CipherFileIO.h new file mode 100644 index 0000000..99aa429 --- /dev/null +++ b/encfs/CipherFileIO.h @@ -0,0 +1,85 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + */ + +#ifndef _CipherFileIO_incl_ +#define _CipherFileIO_incl_ + +#include "BlockFileIO.h" +#include "CipherKey.h" + +#include + +class Cipher; + +/* + Implement the FileIO interface encrypting data in blocks. + + Uses BlockFileIO to handle the block scatter / gather issues. +*/ +class CipherFileIO : public BlockFileIO +{ +public: + CipherFileIO( const boost::shared_ptr &base, + const boost::shared_ptr &cipher, + const CipherKey &key, int blockSize, + bool uniqueIV, bool reverseEncryption ); + virtual ~CipherFileIO(); + + virtual rel::Interface interface() const; + + virtual void setFileName( const char *fileName ); + virtual const char *getFileName() const; + virtual bool setIV( uint64_t iv ); + + virtual int open( int flags ); + + virtual int getAttr( struct stat *stbuf ) const; + virtual off_t getSize() const; + + virtual int truncate( off_t size ); + + virtual bool isWritable() const; + +private: + virtual ssize_t readOneBlock( const IORequest &req ) const; + virtual bool writeOneBlock( const IORequest &req ); + + void initHeader(); + bool writeHeader(); + bool blockRead( unsigned char *buf, int size, + uint64_t iv64 ) const; + bool streamRead( unsigned char *buf, int size, + uint64_t iv64 ) const; + bool blockWrite( unsigned char *buf, int size, + uint64_t iv64 ) const; + bool streamWrite( unsigned char *buf, int size, + uint64_t iv64 ) const; + + boost::shared_ptr base; + boost::shared_ptr cipher; + CipherKey key; + // if haveHeader is true, then we have a transparent file header which + // contains a 64 bit initialization vector. + bool haveHeader; + bool externalIVChaining; + uint64_t externalIV; + uint64_t fileIV; + int lastFlags; + bool reverseEncryption; +}; + +#endif diff --git a/encfs/CipherKey.cpp b/encfs/CipherKey.cpp new file mode 100644 index 0000000..b85e1f1 --- /dev/null +++ b/encfs/CipherKey.cpp @@ -0,0 +1,30 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2007, Valient Gough + * + * This program is free software: you can 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 . + */ + +#include "CipherKey.h" + +AbstractCipherKey::AbstractCipherKey() +{ +} + +AbstractCipherKey::~AbstractCipherKey() +{ +} + diff --git a/encfs/CipherKey.h b/encfs/CipherKey.h new file mode 100644 index 0000000..9c88cd7 --- /dev/null +++ b/encfs/CipherKey.h @@ -0,0 +1,36 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2007, Valient Gough + * + * This program is free software: you can 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 . + */ + +#ifndef _CipherKey_incl_ +#define _CipherKey_incl_ + +#include + +class AbstractCipherKey +{ +public: + AbstractCipherKey(); + virtual ~AbstractCipherKey(); +}; + +typedef boost::shared_ptr CipherKey; + +#endif + diff --git a/encfs/ConfigReader.cpp b/encfs/ConfigReader.cpp new file mode 100644 index 0000000..439ba28 --- /dev/null +++ b/encfs/ConfigReader.cpp @@ -0,0 +1,159 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This program is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include "ConfigReader.h" + +#include + +#include +#include +#include +#include +#include + + +using namespace std; +using namespace rlog; + + +ConfigReader::ConfigReader() +{ +} + +ConfigReader::~ConfigReader() +{ +} + +// read the entire file into a ConfigVar instance and then use that to decode +// into mapped variables. +bool +ConfigReader::load(const char *fileName) +{ + struct stat stbuf; + memset( &stbuf, 0, sizeof(struct stat)); + if( lstat( fileName, &stbuf ) != 0) + return false; + + int size = stbuf.st_size; + + int fd = open( fileName, O_RDONLY ); + if(fd < 0) + return false; + + char *buf = new char[size]; + + int res = ::read( fd, buf, size ); + close( fd ); + + if( res != size ) + { + rWarning("Partial read of config file, expecting %i bytes, got %i", + size, res); + delete[] buf; + return false; + } + + ConfigVar in; + in.write( (unsigned char *)buf, size ); + delete[] buf; + + return loadFromVar( in ); +} + +bool +ConfigReader::loadFromVar(ConfigVar &in) +{ + in.resetOffset(); + + // parse. + int numEntries = in.readInt(); + + for(int i=0; i> key >> value; + + if(key.length() == 0) + { + rError("Invalid key encoding in buffer"); + return false; + } + ConfigVar newVar( value ); + vars.insert( make_pair( key, newVar ) ); + } + + return true; +} + +bool +ConfigReader::save(const char *fileName) const +{ + // write everything to a ConfigVar, then output to disk + ConfigVar out = toVar(); + + int fd = ::open( fileName, O_RDWR | O_CREAT, 0640 ); + if(fd >= 0) + { + int retVal = ::write( fd, out.buffer(), out.size() ); + close( fd ); + if(retVal != out.size()) + { + rError("Error writing to config file %s", fileName); + return false; + } + } else + { + rError("Unable to open or create file %s", fileName); + return false; + } + + return true; +} + +ConfigVar +ConfigReader::toVar() const +{ + // write everything to a ConfigVar, then output to disk + ConfigVar out; + out.writeInt( vars.size() ); + map::const_iterator it; + for(it = vars.begin(); it != vars.end(); ++it) + { + out.writeInt( it->first.size() ); + out.write( (unsigned char*)it->first.data(), it->first.size() ); + out.writeInt( it->second.size() ); + out.write( (unsigned char*)it->second.buffer(), it->second.size() ); + } + + return out; +} + +ConfigVar ConfigReader::operator[] ( const std::string &varName ) const +{ + // read only + map::const_iterator it = vars.find( varName ); + if( it == vars.end() ) + return ConfigVar(); + else + return it->second; +} + +ConfigVar &ConfigReader::operator[] ( const std::string &varName ) +{ + return vars[ varName ]; +} + diff --git a/encfs/ConfigReader.h b/encfs/ConfigReader.h new file mode 100644 index 0000000..c1e68bc --- /dev/null +++ b/encfs/ConfigReader.h @@ -0,0 +1,63 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This program is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef _ConfigReader_incl_ +#define _ConfigReader_incl_ + +#include +#include + +#include "ConfigVar.h" + +/* + handles Configuration load / store for Encfs filesystems. + + loading existing config file example: + + ConfigReader cfg; + cfg.load( filesystemConfigFile ); + + Interface iface; + cfg["cipher"] >> iface; + + + creating new config example: + + ConfigReader cfg; + cfg["cipher"] << cipher->interface(); +*/ +class ConfigReader +{ +public: + ConfigReader(); + ~ConfigReader(); + + bool load(const char *fileName); + bool save(const char *fileName) const; + + ConfigVar toVar() const; + bool loadFromVar( ConfigVar &var ); + + ConfigVar operator[](const std::string &varName) const; + ConfigVar &operator[](const std::string &varName); + +private: + std::map vars; +}; + + +#endif diff --git a/encfs/ConfigVar.cpp b/encfs/ConfigVar.cpp new file mode 100644 index 0000000..47aaa4e --- /dev/null +++ b/encfs/ConfigVar.cpp @@ -0,0 +1,247 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This program is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include "ConfigVar.h" +#include + +using namespace rlog; + +#ifndef MIN +inline int MIN(int a, int b) +{ + return (a < b) ? a : b; +} +#endif + + +ConfigVar::ConfigVar() + : pd( new ConfigVarData ) +{ + pd->offset = 0; +} + +ConfigVar::ConfigVar(const std::string &buf) + : pd( new ConfigVarData ) +{ + pd->buffer = buf; + pd->offset = 0; +} + +ConfigVar::ConfigVar(const ConfigVar &src) +{ + pd = src.pd; +} + +ConfigVar::~ConfigVar() +{ + pd.reset(); +} + +ConfigVar & ConfigVar::operator = (const ConfigVar &src) +{ + if(src.pd == pd) + return *this; + else + pd = src.pd; + + return *this; +} + +void ConfigVar::resetOffset() +{ + pd->offset = 0; +} + +int ConfigVar::read(unsigned char *buffer_, int bytes) const +{ + int toCopy = MIN( bytes, pd->buffer.size() - pd->offset ); + + if(toCopy > 0) + memcpy( buffer_, pd->buffer.data() + pd->offset, toCopy ); + + pd->offset += toCopy; + + return toCopy; +} + +int ConfigVar::write(const unsigned char *data, int bytes) +{ + if(pd->buffer.size() == (unsigned int)pd->offset) + { + pd->buffer.append( (const char *)data, bytes ); + } else + { + pd->buffer.insert( pd->offset, (const char *)data, bytes ); + } + + pd->offset += bytes; + + return bytes; +} + +int ConfigVar::size() const +{ + return pd->buffer.size(); +} + +const char *ConfigVar::buffer() const +{ + return pd->buffer.data(); +} + +int ConfigVar::at() const +{ + return pd->offset; +} + +void ConfigVar::writeString(const char *data, int bytes) +{ + writeInt( bytes ); + write( (const unsigned char *)data, bytes ); +} + + +// convert integer to BER encoded integer +void ConfigVar::writeInt(int val) +{ + // we can represent 7 bits per char output, so a 32bit number may take up + // to 5 bytes. + // first byte: 0x0000007f 0111,1111 + // second byte: 0x00003f80 0011,1111 1000,0000 + // third byte: 0x001fb000 0000,0000 0001,1111 1100,0000 0000,0000 + // fourth byte: 0x0fe00000 0000,1111 1110,0000 + // fifth byte: 0xf0000000 1111,0000 + unsigned char digit[5]; + + digit[4] = (unsigned char)((val & 0x0000007f)); + digit[3] = 0x80 | (unsigned char)((val & 0x00003f80) >> 7); + digit[2] = 0x80 | (unsigned char)((val & 0x001fc000) >> 14); + digit[1] = 0x80 | (unsigned char)((val & 0x0fe00000) >> 21); + digit[0] = 0x80 | (unsigned char)((val & 0xf0000000) >> 28); + + // find the starting point - we only need to output starting at the most + // significant non-zero digit.. + int start = 0; + while(digit[start] == 0x80) + ++start; + + write( digit + start, 5-start ); +} + +int ConfigVar::readInt() const +{ + const unsigned char * buf = (const unsigned char *)buffer(); + int bytes = this->size(); + int offset = at(); + int value = 0; + bool highBitSet; + + rAssert( offset < bytes ); + + do + { + unsigned char tmp = buf[offset++]; + highBitSet = tmp & 0x80; + + value = (value << 7) | (int)(tmp & 0x7f); + } while(highBitSet && offset < bytes); + + pd->offset = offset; + + // should never end up with a negative number.. + rAssert( value >= 0 ); + + return value; +} + +int ConfigVar::readInt( int defaultValue ) const +{ + int bytes = this->size(); + int offset = at(); + + if(offset >= bytes) + return defaultValue; + else + return readInt(); +} + +bool ConfigVar::readBool( bool defaultValue ) const +{ + int tmp = readInt( defaultValue ? 1 : 0 ); + return (tmp != 0); +} + +ConfigVar & operator << (ConfigVar &src, bool value) +{ + src.writeInt( value ? 1 : 0 ); + return src; +} + +ConfigVar & operator << (ConfigVar &src, int var) +{ + src.writeInt( var ); + return src; +} + +ConfigVar & operator << (ConfigVar &src, const std::string &str) +{ + src.writeString( str.data(), str.length() ); + return src; +} + +const ConfigVar & operator >> (const ConfigVar &src, bool &result) +{ + int tmp = src.readInt(); + result = (tmp != 0); + return src; +} + +const ConfigVar & operator >> (const ConfigVar &src, int &result) +{ + result = src.readInt(); + return src; +} + +const ConfigVar & operator >> (const ConfigVar &src, std::string &result) +{ + int length = src.readInt(); + //rAssert(length > 0); + + int readLen; + + unsigned char tmpBuf[32]; + if(length > (int)sizeof(tmpBuf)) + { + unsigned char *ptr = new unsigned char[length]; + readLen = src.read( ptr, length ); + result.assign( (char*)ptr, length ); + delete[] ptr; + } else + { + readLen = src.read( tmpBuf, length ); + result.assign( (char*)tmpBuf, length ); + } + + if(readLen != length) + { + rDebug("string encoded as size %i bytes, read %i", length, readLen ); + } + rAssert(readLen == length); + + return src; +} + diff --git a/encfs/ConfigVar.h b/encfs/ConfigVar.h new file mode 100644 index 0000000..0c8ca04 --- /dev/null +++ b/encfs/ConfigVar.h @@ -0,0 +1,80 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This program is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef _ConfigVar_incl_ +#define _ConfigVar_incl_ + +#include +#include + +using boost::shared_ptr; + +class ConfigVar +{ + struct ConfigVarData + { + std::string buffer; + int offset; + }; + + shared_ptr pd; + +public: + ConfigVar(); + ConfigVar(const std::string &buffer); + ConfigVar(const ConfigVar &src); + ~ConfigVar(); + + ConfigVar & operator = (const ConfigVar &src); + + // reset read/write offset.. + void resetOffset(); + + // read bytes + int read(unsigned char *buffer, int size) const; + + // write bytes.. + int write(const unsigned char *data, int size); + + int readInt() const; + int readInt( int defaultValue ) const; + void writeInt(int value); + + bool readBool( bool defaultValue ) const; + + void writeString(const char *data, int size); + + // return amount of data in var + int size() const; + // return data pointer - returns front of data pointer, not the current + // position. + const char *buffer() const; + + // return current position in data() buffer. + int at() const; +}; + +ConfigVar & operator << (ConfigVar &, bool); +ConfigVar & operator << (ConfigVar &, int); +ConfigVar & operator << (ConfigVar &, const std::string &str); + +const ConfigVar & operator >> (const ConfigVar &, bool &); +const ConfigVar & operator >> (const ConfigVar &, int &); +const ConfigVar & operator >> (const ConfigVar &, std::string &str); + +#endif + diff --git a/encfs/Context.cpp b/encfs/Context.cpp new file mode 100644 index 0000000..03c455b --- /dev/null +++ b/encfs/Context.cpp @@ -0,0 +1,182 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2007, Valient Gough + * + * This program is free software: you can 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 . + */ + +#include "FileNode.h" +#include "Context.h" +#include "Mutex.h" +#include "FileUtils.h" + +#include + +using namespace rel; +using namespace rlog; + +EncFS_Context::EncFS_Context() +{ + pthread_cond_init( &wakeupCond, 0 ); + pthread_mutex_init( &wakeupMutex, 0 ); + pthread_mutex_init( &contextMutex, 0 ); + + usageCount = 0; +} + +EncFS_Context::~EncFS_Context() +{ + pthread_mutex_destroy( &contextMutex ); + pthread_mutex_destroy( &wakeupMutex ); + pthread_cond_destroy( &wakeupCond ); + + // release all entries from map + openFiles.clear(); +} + +shared_ptr EncFS_Context::getRoot(int *errCode) +{ + shared_ptr ret; + do + { + { + Lock lock( contextMutex ); + ret = root; + ++usageCount; + } + + if(!ret) + { + int res = remountFS( this ); + if(res != 0) + { + *errCode = res; + break; + } + } + } while(!ret); + + return ret; +} + +void EncFS_Context::setRoot(const shared_ptr &r) +{ + Lock lock( contextMutex ); + + root = r; + if(r) + rootCipherDir = r->rootDirectory(); +} + +bool EncFS_Context::isMounted() +{ + return root; +} + +int EncFS_Context::getAndResetUsageCounter() +{ + Lock lock( contextMutex ); + + int count = usageCount; + usageCount = 0; + + return count; +} + +int EncFS_Context::openFileCount() const +{ + Lock lock( contextMutex ); + + return openFiles.size(); +} + +shared_ptr EncFS_Context::lookupNode(const char *path) +{ + Lock lock( contextMutex ); + + FileMap::iterator it = openFiles.find( std::string(path) ); + if(it != openFiles.end()) + { + rInfo("found existing node for %s", path); + // all the items in the set point to the same node.. so just use the + // first + return (*it->second.begin())->node; + } else + { + rInfo("no node found for %s", path); + return shared_ptr(); + } +} + +void EncFS_Context::renameNode(const char *from, const char *to) +{ + Lock lock( contextMutex ); + + FileMap::iterator it = openFiles.find( std::string(from) ); + if(it != openFiles.end()) + { + std::set val = it->second; + openFiles.erase(it); + openFiles[ std::string(to) ] = val; + } +} + +shared_ptr EncFS_Context::getNode(void *pl) +{ + Placeholder *ph = (Placeholder*)pl; + return ph->node; +} + +void *EncFS_Context::putNode(const char *path, + const boost::shared_ptr &node) +{ + Lock lock( contextMutex ); + Placeholder *pl = new Placeholder( node ); + openFiles[ std::string(path) ].insert(pl); + + rInfo("added open node record for %s", path); + + return (void *)pl; +} + +void EncFS_Context::eraseNode(const char *path, void *pl) +{ + Lock lock( contextMutex ); + + Placeholder *ph = (Placeholder *)pl; + + FileMap::iterator it = openFiles.find( std::string(path) ); + rAssert(it != openFiles.end()); + + int rmCount = it->second.erase( ph ); + + rAssert(rmCount == 1); + + rInfo("released open node record for %s", path); + // if no more references to this file, remove the record all together + if(it->second.empty()) + { + rInfo("last open node closed for %s", path); + // attempts to make use of shallow copy to clear memory used to hold + // unencrypted filenames.. not sure this does any good.. + std::string storedName = it->first; + openFiles.erase( it ); + storedName.assign( storedName.length(), '\0' ); + } + + delete ph; +} + diff --git a/encfs/Context.h b/encfs/Context.h new file mode 100644 index 0000000..5a77c08 --- /dev/null +++ b/encfs/Context.h @@ -0,0 +1,106 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2007, Valient Gough + * + * This program is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef _Context_incl_ +#define _Context_incl_ + +#include "encfs.h" +#include +#include + +#ifdef USE_HASHMAP +#include +#else +#include +#endif + +using boost::shared_ptr; +struct EncFS_Args; +struct EncFS_Opts; + +class EncFS_Context +{ +public: + EncFS_Context(); + ~EncFS_Context(); + + shared_ptr getNode(void *ptr); + shared_ptr lookupNode(const char *path); + + int getAndResetUsageCounter(); + int openFileCount() const; + + void *putNode(const char *path, const shared_ptr &node); + + void eraseNode(const char *path, void *placeholder); + + void renameNode(const char *oldName, const char *newName); + + void setRoot(const shared_ptr &root); + shared_ptr getRoot(int *err); + bool isMounted(); + + shared_ptr args; + shared_ptr opts; + bool publicFilesystem; + + // root path to cipher dir + std::string rootCipherDir; + + // for idle monitor + bool running; + pthread_t monitorThread; + pthread_cond_t wakeupCond; + pthread_mutex_t wakeupMutex; + +private: + /* This placeholder is what is referenced in FUSE context (passed to + * callbacks). + * + * A FileNode may be opened many times, but only one FileNode instance per + * file is kept. Rather then doing reference counting in FileNode, we + * store a unique Placeholder for each open() until the corresponding + * release() is called. shared_ptr then does our reference counting for + * us. + */ + struct Placeholder + { + shared_ptr node; + + Placeholder( const shared_ptr &ptr ) : node(ptr) {} + }; + + // set of open files, indexed by path +#ifdef USE_HASHMAP + typedef __gnu_cxx::hash_map > FileMap; +#else + typedef std::map< std::string, + std::set > FileMap; +#endif + + mutable pthread_mutex_t contextMutex; + FileMap openFiles; + + int usageCount; + shared_ptr root; +}; + +int remountFS( EncFS_Context *ctx ); + +#endif + diff --git a/encfs/DirNode.cpp b/encfs/DirNode.cpp new file mode 100644 index 0000000..1c5bf75 --- /dev/null +++ b/encfs/DirNode.cpp @@ -0,0 +1,808 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2003-2004, Valient Gough + * + * This program is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include "encfs.h" + +#include "DirNode.h" +#include "FileUtils.h" + +#include +#include +#include +#include +#include +#include +#include +#ifdef linux +#include +#endif + +#include "Context.h" +#include "Cipher.h" +#include "Mutex.h" +#include +#include + +#include + +using namespace std; +using namespace rel; +using namespace rlog; + +static RLogChannel *Info = DEF_CHANNEL( "info/DirNode", Log_Info ); + +class DirDeleter +{ +public: + void operator () ( DIR *d ) + { + ::closedir( d ); + } +}; + + +DirTraverse::DirTraverse(const shared_ptr &_dirPtr, + uint64_t _iv, const shared_ptr &_naming) + : dir( _dirPtr ) + , iv( _iv ) + , naming( _naming ) +{ +} + +DirTraverse::DirTraverse(const DirTraverse &src) + : dir( src.dir ) + , iv( src.iv ) + , naming( src.naming ) +{ +} + +DirTraverse &DirTraverse::operator = (const DirTraverse &src) +{ + dir = src.dir; + iv = src.iv; + naming = src.naming; + + return *this; +} + +DirTraverse::~DirTraverse() +{ + dir.reset(); + iv = 0; + naming.reset(); +} + +static +bool _nextName(struct dirent *&de, const shared_ptr &dir, + int *fileType, ino_t *inode) +{ + de = ::readdir( dir.get() ); + + if(de) + { + if(fileType) + { +#if defined(_DIRENT_HAVE_D_TYPE) || defined(__FreeBSD__) + *fileType = de->d_type; +#else +#warning "struct dirent.d_type not supported" + *fileType = 0; +#endif + } + if(inode) + *inode = de->d_ino; + return true; + } else + { + if(fileType) + *fileType = 0; + return false; + } +} + + +std::string DirTraverse::nextPlaintextName(int *fileType, ino_t *inode) +{ + struct dirent *de=0; + while(_nextName(de, dir, fileType, inode)) + { + try + { + uint64_t localIv = iv; + return naming->decodePath( de->d_name, &localIv ); + } catch ( rlog::Error &ex ) + { + // .. .problem decoding, ignore it and continue on to next name.. + rDebug("error decoding filename: %s", de->d_name); + } + } + + return string(); +} + +std::string DirTraverse::nextInvalid() +{ + struct dirent *de=0; + // find the first name which produces a decoding error... + while(_nextName(de, dir, (int*)0, (ino_t*)0)) + { + try + { + uint64_t localIv = iv; + naming->decodePath( de->d_name, &localIv ); + continue; + } catch( rlog::Error &ex ) + { + return string( de->d_name ); + } + } + + return string(); +} + +struct RenameEl +{ + // ciphertext names + string oldCName; + string newCName; // intermediate name (not final cname) + + // plaintext names + string oldPName; + string newPName; + + bool isDirectory; +}; + +class RenameOp +{ +private: + DirNode *dn; + shared_ptr< list > renameList; + list::const_iterator last; + +public: + RenameOp( DirNode *_dn, const shared_ptr< list > &_renameList ) + : dn(_dn), renameList(_renameList) + { + last = renameList->begin(); + } + + RenameOp(const RenameOp &src) + : dn(src.dn) + , renameList(src.renameList) + , last(src.last) + { + } + + ~RenameOp() + { + if(renameList) + { + // got a bunch of decoded filenames sitting in memory.. do a little + // cleanup before leaving.. + list::iterator it; + for(it = renameList->begin(); it != renameList->end(); ++it) + { + it->oldPName.assign( it->oldPName.size(), ' ' ); + it->newPName.assign( it->newPName.size(), ' ' ); + } + } + } + + void reset() + { + renameList.reset(); + } + + operator bool () const + { + return renameList; + } + + bool apply() + { + try + { + while(last != renameList->end()) + { + // backing store rename. + rDebug("renaming %s -> %s", + last->oldCName.c_str(), last->newCName.c_str()); + + // internal node rename.. + dn->renameNode( last->oldPName.c_str(), + last->newPName.c_str() ); + + // rename on disk.. + if(::rename( last->oldCName.c_str(), + last->newCName.c_str() ) == -1) + { + rWarning("Error renaming %s: %s", + last->oldCName.c_str(), strerror( errno )); + dn->renameNode( last->newPName.c_str(), + last->oldPName.c_str(), false ); + return false; + } + + ++last; + } + + return true; + } catch( rlog::Error &err ) + { + err.log( _RLWarningChannel ); + return false; + } + } + + void undo() + { + rDebug("in undoRename"); + + if(last == renameList->begin()) + { + rDebug("nothing to undo"); + return; // nothing to undo + } + + // list has to be processed backwards, otherwise we may rename + // directories and directory contents in the wrong order! + int undoCount = 0; + list::const_iterator it = last; + + while( it != renameList->begin() ) + { + --it; + + rDebug("undo: renaming %s -> %s", + it->newCName.c_str(), it->oldCName.c_str()); + + ::rename( it->newCName.c_str(), it->oldCName.c_str() ); + try + { + dn->renameNode( it->newPName.c_str(), + it->oldPName.c_str(), false ); + } catch( rlog::Error &err ) + { + err.log( _RLWarningChannel ); + // continue on anyway... + } + ++undoCount; + }; + + rWarning("Undo rename count: %i", undoCount); + } +}; + +DirNode::DirNode(EncFS_Context *_ctx, + const string &sourceDir, const shared_ptr &_config) +{ + pthread_mutex_init( &mutex, 0 ); + + Lock _lock( mutex ); + + ctx = _ctx; + rootDir = sourceDir; + config = _config; + + // make sure rootDir ends in '/', so that we can form a path by appending + // the rest.. + if( rootDir[ rootDir.length()-1 ] != '/' ) + rootDir.append( 1, '/'); + + naming = config->nameCoding; +} + +DirNode::~DirNode() +{ +} + +bool +DirNode::hasDirectoryNameDependency() const +{ + return naming ? naming->getChainedNameIV() : false; +} + +string +DirNode::rootDirectory() +{ + // don't update last access here, otherwise 'du' would cause lastAccess to + // be reset. + // chop off '/' terminator from root dir. + return string( rootDir, 0, rootDir.length()-1 ); +} + +string +DirNode::cipherPath( const char *plaintextPath ) +{ + return rootDir + naming->encodePath( plaintextPath ); +} + +string +DirNode::plainPath( const char *cipherPath_ ) +{ + try + { + if( !strncmp( cipherPath_, rootDir.c_str(), + rootDir.length() ) ) + { + return naming->decodePath( cipherPath_ + rootDir.length() ); + } else + { + if ( cipherPath_[0] == '+' ) + { + // decode as fully qualified path + return string("/") + naming->decodeName( cipherPath_+1, + strlen(cipherPath_+1) ); + } else + { + return naming->decodePath( cipherPath_ ); + } + } + + } catch( rlog::Error &err ) + { + rError("decode err: %s", err.message()); + err.log( _RLWarningChannel ); + + return string(); + } +} + +string +DirNode::relativeCipherPath( const char *plaintextPath ) +{ + try + { + if(plaintextPath[0] == '/') + { + // mark with '+' to indicate special decoding.. + return string("+") + naming->encodeName(plaintextPath+1, + strlen(plaintextPath+1)); + } else + { + return naming->encodePath( plaintextPath ); + } + } catch( rlog::Error &err ) + { + rError("encode err: %s", err.message()); + err.log( _RLWarningChannel ); + + return string(); + } +} + +DirTraverse DirNode::openDir(const char *plaintextPath) +{ + string cyName = rootDir + naming->encodePath( plaintextPath ); + //rDebug("openDir on %s", cyName.c_str() ); + + shared_ptr dp( ::opendir( cyName.c_str() ), DirDeleter() ); + if(!dp) + { + rDebug("opendir error %s", strerror(errno)); + return DirTraverse( dp, 0, shared_ptr() ); + } else + { + uint64_t iv = 0; + // if we're using chained IV mode, then compute the IV at this + // directory level.. + try + { + if( naming->getChainedNameIV() ) + naming->encodePath( plaintextPath, &iv ); + } catch( rlog::Error &err ) + { + rError("encode err: %s", err.message()); + err.log( _RLWarningChannel ); + } + return DirTraverse( dp, iv, naming ); + } +} + +bool DirNode::genRenameList( list &renameList, const char *fromP, + const char *toP ) +{ + uint64_t fromIV = 0, toIV = 0; + + // compute the IV for both paths + string fromCPart = naming->encodePath( fromP, &fromIV ); + string toCPart = naming->encodePath( toP, &toIV ); + + // where the files live before the rename.. + string sourcePath = rootDir + fromCPart; + + // ok..... we wish it was so simple.. should almost never happen + if(fromIV == toIV) + return true; + + // generate the real destination path, where we expect to find the files.. + rDebug("opendir %s", sourcePath.c_str() ); + shared_ptr dir = shared_ptr( + opendir( sourcePath.c_str() ), DirDeleter() ); + if(!dir) + return false; + + struct dirent *de = NULL; + while((de = ::readdir( dir.get() )) != NULL) + { + // decode the name using the oldIV + uint64_t localIV = fromIV; + string plainName; + + if((de->d_name[0] == '.') && + ((de->d_name[1] == '\0') + || ((de->d_name[1] == '.') && (de->d_name[2] == '\0')))) + { + // skip "." and ".." + continue; + } + + try + { + plainName = naming->decodePath( de->d_name, &localIV ); + } catch( rlog::Error &ex ) + { + // if filename can't be decoded, then ignore it.. + continue; + } + + // any error in the following will trigger a rename failure. + try + { + // re-encode using the new IV.. + localIV = toIV; + string newName = naming->encodePath( plainName.c_str(), &localIV ); + + // store rename information.. + string oldFull = sourcePath + '/' + de->d_name; + string newFull = sourcePath + '/' + newName; + + RenameEl ren; + ren.oldCName = oldFull; + ren.newCName = newFull; + ren.oldPName = string(fromP) + '/' + plainName; + ren.newPName = string(toP) + '/' + plainName; + + bool isDir; +#if defined(_DIRENT_HAVE_D_TYPE) + if(de->d_type != DT_UNKNOWN) + { + isDir = (de->d_type == DT_DIR); + } else +#endif + { + isDir = isDirectory( oldFull.c_str() ); + } + + ren.isDirectory = isDir; + + if(isDir) + { + // recurse.. We want to add subdirectory elements before the + // parent, as that is the logical rename order.. + if(!genRenameList( renameList, + ren.oldPName.c_str(), + ren.newPName.c_str())) + { + return false; + } + } + + rDebug("adding file %s to rename list", + oldFull.c_str()); + + renameList.push_back( ren ); + + } catch( rlog::Error &err ) + { + // We can't convert this name, because we don't have a valid IV for + // it (or perhaps a valid key).. It will be inaccessible.. + rWarning("Aborting rename: error on file: %s", + fromCPart.append(1, '/').append(de->d_name).c_str()); + err.log( _RLDebugChannel ); + + // abort.. Err on the side of safety and disallow rename, rather + // then loosing files.. + return false; + } + } + + return true; +} + + +/* + A bit of a pain.. If a directory is renamed in a filesystem with + directory initialization vector chaining, then we have to recursively + rename every descendent of this directory, as all initialization vectors + will have changed.. + + Returns a list of renamed items on success, a null list on failure. +*/ +shared_ptr +DirNode::newRenameOp( const char *fromP, const char *toP ) +{ + // Do the rename in two stages to avoid chasing our tail + // Undo everything if we encounter an error! + shared_ptr< list > renameList(new list); + shared_ptr op( new RenameOp(this, renameList) ); + if(!genRenameList( *renameList.get(), fromP, toP )) + { + rWarning("Error during generation of recursive rename list"); + op.reset(); + } + + return op; +} + + +int DirNode::mkdir(const char *plaintextPath, mode_t mode, + uid_t uid, gid_t gid) +{ + string cyName = rootDir + naming->encodePath( plaintextPath ); + rAssert( !cyName.empty() ); + + rLog( Info, "mkdir on %s", cyName.c_str() ); + + // if uid or gid are set, then that should be the directory owner + int olduid = -1; + int oldgid = -1; + if(uid != 0) + olduid = setfsuid( uid ); + if(gid != 0) + oldgid = setfsgid( gid ); + + int res = ::mkdir( cyName.c_str(), mode ); + + if(olduid >= 0) + setfsuid( olduid ); + if(oldgid >= 0) + setfsgid( oldgid ); + + if(res == -1) + { + int eno = errno; + rWarning("mkdir error on %s mode %i: %s", cyName.c_str(), + mode, strerror(eno)); + res = -eno; + } else + res = 0; + + return res; +} + +int +DirNode::rename( const char *fromPlaintext, const char *toPlaintext ) +{ + Lock _lock( mutex ); + + string fromCName = rootDir + naming->encodePath( fromPlaintext ); + string toCName = rootDir + naming->encodePath( toPlaintext ); + rAssert( !fromCName.empty() ); + rAssert( !toCName.empty() ); + + rLog( Info, "rename %s -> %s", fromCName.c_str(), toCName.c_str() ); + + shared_ptr toNode = findOrCreate( toPlaintext ); + + shared_ptr renameOp; + if( hasDirectoryNameDependency() && isDirectory( fromCName.c_str() )) + { + rLog( Info, "recursive rename begin" ); + renameOp = newRenameOp( fromPlaintext, toPlaintext ); + + if(!renameOp || !renameOp->apply()) + { + if(renameOp) + renameOp->undo(); + + rWarning("rename aborted"); + return -EACCES; + } + rLog( Info, "recursive rename end" ); + } + + int res = 0; + try + { + renameNode( fromPlaintext, toPlaintext ); + res = ::rename( fromCName.c_str(), toCName.c_str() ); + + if(res == -1) + { + // undo + res = -errno; + renameNode( toPlaintext, fromPlaintext, false ); + + if(renameOp) + renameOp->undo(); + } + } catch( rlog::Error &err ) + { + // exception from renameNode, just show the error and continue.. + err.log( _RLWarningChannel ); + res = -EIO; + } + + if(res != 0) + { + rLog( Info, "rename failed: %s", strerror( errno )); + res = -errno; + } + + return res; +} + +int DirNode::link( const char *from, const char *to ) +{ + Lock _lock( mutex ); + + string fromCName = rootDir + naming->encodePath( from ); + string toCName = rootDir + naming->encodePath( to ); + + rAssert( !fromCName.empty() ); + rAssert( !toCName.empty() ); + + rLog(Info, "link %s -> %s", fromCName.c_str(), toCName.c_str()); + + int res = -EPERM; + if( config->externalIVChaining ) + { + rLog(Info, "hard links not supported with external IV chaining!"); + } else + { + res = ::link( fromCName.c_str(), toCName.c_str() ); + if(res == -1) + res = -errno; + else + res = 0; + } + + return res; +} + +/* + The node is keyed by filename, so a rename means the internal node names + must be changed. +*/ +shared_ptr DirNode::renameNode( const char *from, const char *to ) +{ + return renameNode( from, to, true ); +} + +shared_ptr DirNode::renameNode( const char *from, const char *to, + bool forwardMode ) +{ + shared_ptr node = findOrCreate( from ); + + if(node) + { + uint64_t newIV = 0; + string cname = rootDir + naming->encodePath( to, &newIV ); + + rLog(Info, "renaming internal node %s -> %s", + node->cipherName(), cname.c_str()); + + if(node->setName( to, cname.c_str(), newIV, forwardMode )) + { + if(ctx) + ctx->renameNode( from, to ); + } else + { + // rename error! - put it back + rError("renameNode failed"); + throw ERROR("Internal node name change failed!"); + } + } + + return node; +} + +shared_ptr DirNode::findOrCreate( const char *plainName) +{ + shared_ptr node; + if(ctx) + node = ctx->lookupNode( plainName ); + + if(!node) + { + uint64_t iv = 0; + string cipherName = naming->encodePath( plainName, &iv ); + node.reset( new FileNode( this, + config->fsSubVersion, + plainName, + (rootDir + cipherName).c_str(), + config->cipher, config->key, + config->blockSize, config->blockMACBytes, + config->blockMACRandBytes, + config->uniqueIV, + config->externalIVChaining, + config->forceDecode, + config->reverseEncryption) ); + + if(config->externalIVChaining) + node->setName(0, 0, iv); + + rLog(Info, "created FileNode for %s", node->cipherName()); + } + + return node; +} + +shared_ptr +DirNode::lookupNode( const char *plainName, const char * requestor ) +{ + (void)requestor; + Lock _lock( mutex ); + + shared_ptr node = findOrCreate( plainName ); + + return node; +} + +/* + Similar to lookupNode, except that we also call open() and only return a + node on sucess.. This is done in one step to avoid any race conditions + with the stored state of the file. +*/ +shared_ptr +DirNode::openNode( const char *plainName, const char * requestor, int flags, + int *result ) +{ + (void)requestor; + rAssert( result != NULL ); + Lock _lock( mutex ); + + shared_ptr node = findOrCreate( plainName ); + + if( node && (*result = node->open( flags )) >= 0 ) + return node; + else + return shared_ptr(); +} + +int DirNode::unlink( const char *plaintextName ) +{ + string cyName = naming->encodePath( plaintextName ); + rLog( Info, "unlink %s", cyName.c_str() ); + + Lock _lock( mutex ); + + int res = 0; + if(ctx && ctx->lookupNode( plaintextName )) + { + // If FUSE is running with "hard_remove" option where it doesn't + // hide open files for us, then we can't allow an unlink of an open + // file.. + rWarning("Refusing to unlink open file: %s, hard_remove option " + "is probably in effect", cyName.c_str() ); + res = -EBUSY; + } else + { + string fullName = rootDir + cyName; + res = ::unlink( fullName.c_str() ); + if(res == -1) + { + res = -errno; + rDebug("unlink error: %s", strerror(errno)); + } + } + + return res; +} + diff --git a/encfs/DirNode.h b/encfs/DirNode.h new file mode 100644 index 0000000..cd7acd7 --- /dev/null +++ b/encfs/DirNode.h @@ -0,0 +1,210 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2003, Valient Gough + * + * This program is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef _DirNode_incl_ +#define _DirNode_incl_ + +#include +#include +#include + +#include +#include +#include +#include + +#include "FileNode.h" +#include "NameIO.h" +#include "CipherKey.h" + +using boost::shared_ptr; + +class Cipher; +class RenameOp; +struct RenameEl; +class EncFS_Context; + +class DirTraverse +{ +public: + DirTraverse(const shared_ptr &dirPtr, uint64_t iv, + const shared_ptr &naming); + DirTraverse(const DirTraverse &src); + ~DirTraverse(); + + DirTraverse &operator = (const DirTraverse &src); + + // returns FALSE to indicate an invalid DirTraverse (such as when + // an invalid directory is requested for traversal) + bool valid() const; + + // return next plaintext filename + // If fileType is not 0, then it is used to return the filetype (or 0 if + // unknown) + std::string nextPlaintextName(int *fileType=0, ino_t *inode=0); + + /* Return cipher name of next undecodable filename.. + The opposite of nextPlaintextName(), as that skips undecodable names.. + */ + std::string nextInvalid(); +private: + + shared_ptr dir; // struct DIR + // initialization vector to use. Not very general purpose, but makes it + // more efficient to support filename IV chaining.. + uint64_t iv; + shared_ptr naming; +}; +inline bool DirTraverse::valid() const { return dir != 0; } + +#ifdef USE_HASHMAP +namespace __gnu_cxx +{ + template<> struct hash + { + size_t operator() (const std::string &__s) const + { + return __stl_hash_string( __s.c_str() ); + } + }; +} +#endif + +class DirNode +{ +public: + struct Config + { + shared_ptr cipher; // cipher to use + CipherKey key; // cipher key to use + shared_ptr nameCoding; // filename encoding implementation + int fsSubVersion; // filesystem version number at creation + int blockSize; // file data block size + bool inactivityTimer; // enables inactivity tracking + int blockMACBytes; // >0 enables per-file-block MAC headers + int blockMACRandBytes; // random bytes in MAC headers + bool uniqueIV; // enable per-file initialization vectors + bool externalIVChaining; + bool forceDecode; // force decoding, even if errors are detected + bool reverseEncryption; + Config() + : fsSubVersion(0) + , blockSize(1) + , inactivityTimer( false ) + , blockMACBytes( 0 ) + , blockMACRandBytes( 0 ) + , uniqueIV( false ) + , externalIVChaining( false ) + , forceDecode( false ) + , reverseEncryption ( false ) + { } + }; + + // sourceDir points to where raw files are stored + DirNode( EncFS_Context *ctx, + const std::string &sourceDir, const shared_ptr &config ); + ~DirNode(); + + // return the path to the root directory + std::string rootDirectory(); + + // find files + shared_ptr lookupNode( const char *plaintextName, + const char *requestor ); + + /* + Combined lookupNode + node->open() call. If the open fails, then the + node is not retained. If the open succeeds, then the node is returned. + */ + shared_ptr openNode( const char *plaintextName, + const char *requestor, int flags, int *openResult ); + + std::string cipherPath( const char *plaintextPath ); + std::string plainPath( const char *cipherPath ); + + // relative cipherPath is the same as cipherPath except that it doesn't + // prepent the mount point. That it, it doesn't return a fully qualified + // name, just a relative path within the encrypted filesystem. + std::string relativeCipherPath( const char *plaintextPath ); + + /* + Returns true if file names are dependent on the parent directory name. + If a directory name is changed, then all the filenames must also be + changed. + */ + bool hasDirectoryNameDependency() const; + + // unlink the specified file + int unlink( const char *plaintextName ); + + // traverse directory + DirTraverse openDir( const char *plainDirName ); + + // uid and gid are used as the directory owner, only if not zero + int mkdir( const char *plaintextPath, mode_t mode, + uid_t uid = 0, gid_t gid = 0); + + int rename( const char *fromPlaintext, const char *toPlaintext ); + + int link( const char *from, const char *to ); + + // returns idle time of filesystem in seconds + int idleSeconds(); + +protected: + + /* + notify that a file is being renamed. + This renames the internal node, if any. If the file is not open, then + this call has no effect. + Returns the FileNode if it was found. + */ + shared_ptr renameNode( const char *from, const char *to ); + shared_ptr renameNode( const char *from, const char *to, + bool forwardMode ); + + /* + when directory IV chaining is enabled, a directory can't be renamed + without renaming all its contents as well. recursiveRename should be + called after renaming the directory, passing in the plaintext from and + to paths. + */ + shared_ptr newRenameOp( const char *from, const char *to ); + +private: + + friend class RenameOp; + + bool genRenameList( std::list &list, const char *fromP, + const char *toP ); + + shared_ptr findOrCreate( const char *plainName); + + pthread_mutex_t mutex; + + EncFS_Context *ctx; + + // passed in as configuration + std::string rootDir; + shared_ptr config; + + // stored here to reduce access through config var.. + shared_ptr naming; +}; + +#endif + diff --git a/encfs/FileIO.cpp b/encfs/FileIO.cpp new file mode 100644 index 0000000..d4da8f3 --- /dev/null +++ b/encfs/FileIO.cpp @@ -0,0 +1,38 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + */ + +#include "FileIO.h" + +FileIO::FileIO() +{ +} + +FileIO::~FileIO() +{ +} + +int FileIO::blockSize() const +{ + return 1; +} + +bool FileIO::setIV( uint64_t iv ) +{ + (void)iv; + return true; +} + diff --git a/encfs/FileIO.h b/encfs/FileIO.h new file mode 100644 index 0000000..1889a27 --- /dev/null +++ b/encfs/FileIO.h @@ -0,0 +1,84 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + */ + +#ifndef _FileIO_incl_ +#define _FileIO_incl_ + +#include "encfs.h" + +#include + +#include "Interface.h" + +struct IORequest +{ + off_t offset; + + // amount of bytes to read/write. + int dataLen; + unsigned char *data; + + IORequest(); +}; + +inline IORequest::IORequest() + : offset(0) + , dataLen(0) + , data(0) +{ +} + +class FileIO +{ +public: + FileIO(); + virtual ~FileIO(); + + virtual rel::Interface interface() const =0; + + // default implementation returns 1, meaning this is not block oriented. + virtual int blockSize() const; + + virtual void setFileName(const char *fileName) =0; + virtual const char *getFileName() const =0; + + // Not sure about this -- it is specific to CipherFileIO, but the + // alternative methods of exposing this interface aren't much nicer.. + virtual bool setIV( uint64_t iv ); + + // open file for specified mode. There is no corresponding close, so a + // file is open until the FileIO interface is destroyed. + virtual int open( int flags ) =0; + + // get filesystem attributes for a file + virtual int getAttr( struct stat *stbuf ) const =0; + virtual off_t getSize( ) const =0; + + virtual ssize_t read( const IORequest &req ) const =0; + virtual bool write( const IORequest &req ) =0; + + virtual int truncate( off_t size ) =0; + + virtual bool isWritable() const =0; +private: + // not implemented.. + FileIO( const FileIO & ); + FileIO &operator = ( const FileIO & ); +}; + +#endif + diff --git a/encfs/FileNode.cpp b/encfs/FileNode.cpp new file mode 100644 index 0000000..6b1f577 --- /dev/null +++ b/encfs/FileNode.cpp @@ -0,0 +1,296 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2003-2004, Valient Gough + * + * This program is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +// Include encfs first, because we need to include fuse.h before any inclusion +// of sys/stat.h or other system headers (to be safe) +#include "encfs.h" + +#include +#include +#include +#include +#include +#ifdef linux +#include +#endif + +#include "config.h" + +#include "FileNode.h" +#include "FileUtils.h" +#include "Cipher.h" +#include "CipherFileIO.h" +#include "RawFileIO.h" +#include "MACFileIO.h" +#include "DirNode.h" + +#include "FileIO.h" +#include "MemoryPool.h" +#include "Mutex.h" + +#include +#include + +using namespace std; +using namespace rel; +using namespace rlog; + +/* + TODO: locking at the FileNode level is inefficient, since this precludes + multiple IO operations from going on concurrently within the same file. + + There is no reason why simultainous reads cannot be satisfied, or why one + read has to wait for the decoding of the previous read before it can be + sent to the IO subsystem! +*/ + +static RLogChannel *Info = DEF_CHANNEL("info/FileNode", Log_Info); + +FileNode::FileNode(DirNode *parent_, + int fsSubVersion, + const char *plaintextName_, const char *cipherName_, + const shared_ptr &dataCipher, const CipherKey &key, + int blockSize, int blockMACBytes, int blockMACRandBytes, bool uniqueIV, + bool externalIVChaining_, bool forceDecode, bool reverseEncryption_ ) +{ + pthread_mutex_init( &mutex, 0 ); + + Lock _lock( mutex ); + + this->_pname = plaintextName_; + this->_cname = cipherName_; + this->parent = parent_; + this->externalIVChaining = externalIVChaining_; + this->reverseEncryption = reverseEncryption_; + + // chain RawFileIO & CipherFileIO + shared_ptr rawIO( new RawFileIO( _cname ) ); + io = shared_ptr( + new CipherFileIO( rawIO, dataCipher, key, blockSize, + uniqueIV, reverseEncryption )); + + if(blockMACBytes) + { + io = shared_ptr(new MACFileIO(io, dataCipher, key, + blockSize,blockMACBytes,blockMACRandBytes,forceDecode)); + } +} + +FileNode::~FileNode() +{ + // FileNode mutex should be locked before the destructor is called + //pthread_mutex_lock( &mutex ); + + _pname.assign( _pname.length(), '\0' ); + _cname.assign( _cname.length(), '\0' ); + io.reset(); + + pthread_mutex_destroy( &mutex ); +} + +const char *FileNode::cipherName() const +{ + return _cname.c_str(); +} + +const char *FileNode::plaintextName() const +{ + return _pname.c_str(); +} + +string FileNode::plaintextParent() const +{ + return parentDirectory( _pname ); +} + +static bool setIV(const shared_ptr &io, uint64_t iv) +{ + struct stat stbuf; + if((io->getAttr(&stbuf) < 0) || S_ISREG(stbuf.st_mode)) + return io->setIV( iv ); + else + return true; +} + +bool FileNode::setName( const char *plaintextName_, const char *cipherName_, + uint64_t iv, bool setIVFirst ) +{ + //Lock _lock( mutex ); + rDebug("calling setIV on %s", cipherName_); + if(setIVFirst) + { + if(externalIVChaining && !setIV(io, iv)) + return false; + + // now change the name.. + if(plaintextName_) + this->_pname = plaintextName_; + if(cipherName_) + { + this->_cname = cipherName_; + io->setFileName( cipherName_ ); + } + } else + { + std::string oldPName = _pname; + std::string oldCName = _cname; + + if(plaintextName_) + this->_pname = plaintextName_; + if(cipherName_) + { + this->_cname = cipherName_; + io->setFileName( cipherName_ ); + } + + if(externalIVChaining && !setIV(io, iv)) + { + _pname = oldPName; + _cname = oldCName; + return false; + } + } + + return true; +} + +int FileNode::mknod(mode_t mode, dev_t rdev, uid_t uid, gid_t gid) +{ + Lock _lock( mutex ); + + int res; + int olduid = -1; + int oldgid = -1; + if(uid != 0) + olduid = setfsuid( uid ); + if(gid != 0) + oldgid = setfsgid( gid ); + + /* + * cf. xmp_mknod() in fusexmp.c + * The regular file stuff could be stripped off if there + * were a create method (advised to have) + */ + if (S_ISREG( mode )) { + res = ::open( _cname.c_str(), O_CREAT | O_EXCL | O_WRONLY, mode ); + if (res >= 0) + res = ::close( res ); + } else if (S_ISFIFO( mode )) + res = ::mkfifo( _cname.c_str(), mode ); + else + res = ::mknod( _cname.c_str(), mode, rdev ); + + if(olduid >= 0) + setfsuid( olduid ); + if(oldgid >= 0) + setfsgid( oldgid ); + + if(res == -1) + { + int eno = errno; + rDebug("mknod error: %s", strerror(eno)); + res = -eno; + } + + return res; +} + +int FileNode::open(int flags) const +{ + Lock _lock( mutex ); + + int res = io->open( flags ); + return res; +} + +int FileNode::getAttr(struct stat *stbuf) const +{ + Lock _lock( mutex ); + + int res = io->getAttr( stbuf ); + return res; +} + +off_t FileNode::getSize() const +{ + Lock _lock( mutex ); + + int res = io->getSize(); + return res; +} + +ssize_t FileNode::read( off_t offset, unsigned char *data, ssize_t size ) const +{ + IORequest req; + req.offset = offset; + req.dataLen = size; + req.data = data; + + Lock _lock( mutex ); + + return io->read( req ); +} + +bool FileNode::write(off_t offset, unsigned char *data, ssize_t size) +{ + rLog(Info, "FileNode::write offset %" PRIi64 ", data size %i", + offset, (int)size); + + IORequest req; + req.offset = offset; + req.dataLen = size; + req.data = data; + + Lock _lock( mutex ); + + return io->write( req ); +} + +int FileNode::truncate( off_t size ) +{ + Lock _lock( mutex ); + + return io->truncate( size ); +} + +int FileNode::sync(bool datasync) +{ + Lock _lock( mutex ); + + int fh = io->open( O_RDONLY ); + if(fh >= 0) + { + int res = -EIO; +#ifdef linux + if(datasync) + res = fdatasync( fh ); + else + res = fsync( fh ); +#else + // no fdatasync support + // TODO: use autoconfig to check for it.. + res = fsync(fh); +#endif + + if(res == -1) + res = -errno; + + return res; + } else + return fh; +} + diff --git a/encfs/FileNode.h b/encfs/FileNode.h new file mode 100644 index 0000000..1232cd7 --- /dev/null +++ b/encfs/FileNode.h @@ -0,0 +1,104 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2003, Valient Gough + * + * This program is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef _FileNode_incl_ +#define _FileNode_incl_ + +#include "encfs.h" +#include "CipherKey.h" + +#include +#include +#include + +class Cipher; +class FileIO; +class DirNode; +using boost::shared_ptr; + +class FileNode +{ +public: + FileNode(DirNode *parent, + int fsSubVersion, // version number for the filesystem + const char *plaintextName, + const char *cipherName, + const shared_ptr &cipher, const CipherKey &key, int blockSize, + int blockMACBytes, // per-block random bytes in header + int blockMACRandBytes, // per-block random bytes in header + bool uniqueIV, // enable per-file initialization vectors + bool externalIVChaining, + bool forceDecode, // decode, even if decoding errors are detected + bool reverseEncryption ); + ~FileNode(); + + const char *plaintextName() const; + const char *cipherName() const; + + // directory portion of plaintextName + std::string plaintextParent() const; + + // if setIVFirst is true, then the IV is changed before the name is changed + // (default). The reverse is also supported for special cases.. + bool setName( const char *plaintextName, const char *cipherName, + uint64_t iv, bool setIVFirst = true); + + // create node + // If uid/gid are not 0, then chown is used change ownership as specified + int mknod(mode_t mode, dev_t rdev, uid_t uid = 0, gid_t gid = 0); + + // Returns < 0 on error (-errno), file descriptor on success. + int open(int flags) const; + + // getAttr returns 0 on success, -errno on failure + int getAttr(struct stat *stbuf) const; + off_t getSize() const; + + ssize_t read(off_t offset, unsigned char *data, ssize_t size) const; + bool write(off_t offset, unsigned char *data, ssize_t size); + + // truncate the file to a particular size + int truncate( off_t size ); + + // datasync or full sync + int sync(bool dataSync); +private: + + // doing locking at the FileNode level isn't as efficient as at the + // lowest level of RawFileIO, since that means locks are held longer + // (held during CPU intensive crypto operations!). However it makes it + // easier to avoid any race conditions with operations such as + // truncate() which may result in multiple calls down to the FileIO + // level. + mutable pthread_mutex_t mutex; + bool externalIVChaining; + bool reverseEncryption; + + shared_ptr io; + std::string _pname; // plaintext name + std::string _cname; // encrypted name + DirNode *parent; + +private: + FileNode(const FileNode &src); + FileNode &operator = (const FileNode &src); + +}; + + +#endif + diff --git a/encfs/FileUtils.cpp b/encfs/FileUtils.cpp new file mode 100644 index 0000000..249b7e6 --- /dev/null +++ b/encfs/FileUtils.cpp @@ -0,0 +1,1438 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This program is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +// defines needed for RedHat 7.3... +#ifdef linux +#define _XOPEN_SOURCE 500 // make sure pwrite() is pulled in +#endif +#define _BSD_SOURCE // pick up setenv on RH7.3 + +#include "encfs.h" +#include "config.h" + +#include "readpassphrase.h" +#include "autosprintf.h" + +#include "FileUtils.h" +#include "ConfigReader.h" + +#include "DirNode.h" +#include "Cipher.h" +#include "StreamNameIO.h" +#include "BlockNameIO.h" +#include "NullNameIO.h" +#include "Context.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "i18n.h" + +using namespace rel; +using namespace rlog; +using namespace std; +using namespace gnu; + +static const int DefaultBlockSize = 512; +// environment variable names for values encfs stores in the environment when +// calling an external password program. +static const char ENCFS_ENV_ROOTDIR[] = "encfs_root"; +static const char ENCFS_ENV_STDOUT[] = "encfs_stdout"; +static const char ENCFS_ENV_STDERR[] = "encfs_stderr"; + + +//static int V5SubVersion = 20040518; +//static int V5SubVersion = 20040621; // add external IV chaining +static int V5SubVersion = 20040813; // fix MACFileIO block size issues +static int V5SubVersionDefault = 0; + +struct ConfigInfo +{ + const char *fileName; + ConfigType type; + const char *environmentOverride; + bool (*loadFunc)(const char *fileName, EncFSConfig *config, + ConfigInfo *cfg); + bool (*saveFunc)(const char *fileName, EncFSConfig *config); + int currentSubVersion; + int defaultSubVersion; +} ConfigFileMapping[] = { + {".encfs5", Config_V5, "ENCFS5_CONFIG", readV5Config, writeV5Config, + V5SubVersion, V5SubVersionDefault }, + // backward compatible support for config versions 3 and 4 + {".encfs4", Config_V4, NULL, readV4Config, writeV4Config, 0, 0 }, + {".encfs3", Config_V3, NULL, readV3Config, writeV3Config, 0, 0 }, + // forget about version 2 and 1 - from before the first public release! + {".encfs2", Config_Prehistoric, NULL, NULL, NULL, 0, 0 }, + {".encfs", Config_Prehistoric, NULL, NULL, NULL, 0, 0 }, + {NULL,Config_None, NULL, NULL, NULL, 0, 0}}; + + +EncFS_Root::EncFS_Root() +{ +} + +EncFS_Root::~EncFS_Root() +{ +} + + +bool fileExists( const char * fileName ) +{ + struct stat buf; + if(!lstat( fileName, &buf )) + { + return true; + } else + { + // XXX show perror? + return false; + } +} + +bool isDirectory( const char *fileName ) +{ + struct stat buf; + if( !lstat( fileName, &buf )) + { + return S_ISDIR( buf.st_mode ); + } else + { + return false; + } +} + +bool isAbsolutePath( const char *fileName ) +{ + if(fileName && fileName[0] != '\0' && fileName[0] == '/') + return true; + else + return false; +} + +const char *lastPathElement( const char *name ) +{ + const char *loc = strrchr( name, '/' ); + return loc ? loc + 1 : name; +} + +std::string parentDirectory( const std::string &path ) +{ + size_t last = path.find_last_of( '/' ); + if(last == string::npos) + return string(""); + else + return path.substr(0, last); +} + +bool userAllowMkdir( const char *path, mode_t mode ) +{ + // TODO: can we internationalize the y/n names? Seems strange to prompt in + // their own language but then have to respond 'y' or 'n'. + // xgroup(setup) + cerr << autosprintf( _("The directory \"%s\" does not exist. Should it be created? (y,n) "), path ); + char answer[10]; + fgets( answer, sizeof(answer), stdin ); + + if(toupper(answer[0]) == 'Y') + { + int result = mkdir( path, mode ); + if(result < 0) + { + perror( _("Unable to create directory: ") ); + return false; + } else + return true; + } else + { + // Directory not created, by user request + cerr << _("Directory not created.") << "\n"; + return false; + } +} + +ConfigType readConfig_load( ConfigInfo *nm, const char *path, + EncFSConfig *config ) +{ + if( nm->loadFunc ) + { + try + { + if( (*nm->loadFunc)( path, config, nm )) + return nm->type; + } catch( rlog::Error & err ) + { + err.log( _RLWarningChannel ); + } + + rError( _("Found config file %s, but failed to load"), path); + return Config_None; + } else + { + // No load function - must be an unsupported type.. + return nm->type; + } +} + +ConfigType readConfig( const string &rootDir, EncFSConfig *config ) +{ + ConfigInfo *nm = ConfigFileMapping; + while(nm->fileName) + { + // allow environment variable to override default config path + if( nm->environmentOverride != NULL ) + { + char *envFile = getenv( nm->environmentOverride ); + if( envFile != NULL ) + return readConfig_load( nm, envFile, config ); + } + // the standard place to look is in the root directory + string path = rootDir + nm->fileName; + if( fileExists( path.c_str() ) ) + return readConfig_load( nm, path.c_str(), config); + + ++nm; + } + + return Config_None; +} + +bool readV5Config( const char *configFile, EncFSConfig *config, + ConfigInfo *info) +{ + bool ok = false; + + // use Config to parse the file and query it.. + ConfigReader cfgRdr; + if(cfgRdr.load( configFile )) + { + try + { + config->subVersion = cfgRdr["subVersion"].readInt( + info->defaultSubVersion ); + if(config->subVersion > info->currentSubVersion) + { + /* config file specifies a version outside our supported + range.. */ + rWarning(_("Config subversion %i found, but this version of" + " encfs only supports up to version %i."), + config->subVersion, info->currentSubVersion); + return false; + } + if( config->subVersion < 20040813 ) + { + rError(_("This version of EncFS doesn't support " + "filesystems created before 2004-08-13")); + return false; + } + + cfgRdr["creator"] >> config->creator; + cfgRdr["cipher"] >> config->cipherIface; + cfgRdr["naming"] >> config->nameIface; + cfgRdr["keySize"] >> config->keySize; + cfgRdr["blockSize"] >> config->blockSize; + cfgRdr["keyData"] >> config->keyData; + config->uniqueIV = cfgRdr["uniqueIV"].readBool( false ); + config->chainedNameIV = cfgRdr["chainedIV"].readBool( false ); + config->externalIVChaining = cfgRdr["externalIV"].readBool( false ); + config->blockMACBytes = cfgRdr["blockMACBytes"].readInt(0); + config->blockMACRandBytes = + cfgRdr["blockMACRandBytes"].readInt(0); + + ok = true; + } catch( rlog::Error &err) + { + err.log( _RLWarningChannel ); + rDebug("Error parsing data in config file %s", configFile); + ok = false; + } + } + + return ok; +} + +bool readV4Config( const char *configFile, EncFSConfig *config, + ConfigInfo *info) +{ + bool ok = false; + + // use Config to parse the file and query it.. + ConfigReader cfgRdr; + if(cfgRdr.load( configFile )) + { + try + { + cfgRdr["cipher"] >> config->cipherIface; + cfgRdr["keySize"] >> config->keySize; + cfgRdr["blockSize"] >> config->blockSize; + cfgRdr["keyData"] >> config->keyData; + + // fill in default for V4 + config->nameIface = Interface("nameio/stream", 1, 0, 0); + config->creator = "EncFS 1.0.x"; + config->subVersion = info->defaultSubVersion; + config->blockMACBytes = 0; + config->blockMACRandBytes = 0; + config->uniqueIV = false; + config->externalIVChaining = false; + config->chainedNameIV = false; + + ok = true; + } catch( rlog::Error &err) + { + err.log( _RLWarningChannel ); + rDebug("Error parsing config file %s", configFile); + ok = false; + } + } + + return ok; +} + +bool readV3Config( const char *configFile, EncFSConfig *config, + ConfigInfo *info) +{ + (void)configFile; + // use Config to parse the file and query it.. + // fill in default for V3 + config->creator = "EncFS 0.x"; + config->subVersion = info->defaultSubVersion; + config->cipherIface = Interface("ssl/blowfish-v0.2", 1, 0, 0); + config->nameIface = Interface("nameio/stream", 0, 1, 0); + config->keySize = 160; + config->blockSize = 64; + config->blockMACBytes = 0; + config->blockMACRandBytes = 0; + config->uniqueIV = false; + config->externalIVChaining = false; + config->chainedNameIV = false; + + bool ok = false; + int vkeyFD = open( configFile, O_RDONLY ); + if( vkeyFD >= 0 ) + { + const int headerSize = 22; // 160 bit (20 bytes) + 2 byte checksum + char keyBuf [ headerSize ]; + + read( vkeyFD, keyBuf, headerSize ); + close( vkeyFD ); + + config->keyData.assign( keyBuf, headerSize ); + ok = true; + } else + { + rDebug("Error opening config file %s", configFile ); + } + + return ok; +} + +bool saveConfig( ConfigType type, const string &rootDir, + EncFSConfig *config ) +{ + bool ok = false; + + ConfigInfo *nm = ConfigFileMapping; + while(nm->fileName) + { + if( nm->type == type && nm->saveFunc ) + { + string path = rootDir + nm->fileName; + if( nm->environmentOverride != NULL ) + { + // use environment file if specified.. + const char *envFile = getenv( nm->environmentOverride ); + if( envFile != NULL ) + path.assign( envFile ); + } + + try + { + ok = (*nm->saveFunc)( path.c_str(), config ); + } catch( rlog::Error &err ) + { + err.log( _RLWarningChannel ); + ok = false; + } + break; + } + ++nm; + } + + return ok; +} + +bool writeV5Config( const char *configFile, EncFSConfig *config ) +{ + ConfigReader cfg; + + cfg["creator"] << config->creator; + cfg["subVersion"] << config->subVersion; + cfg["cipher"] << config->cipherIface; + cfg["naming"] << config->nameIface; + cfg["keySize"] << config->keySize; + cfg["blockSize"] << config->blockSize; + cfg["keyData"] << config->keyData; + cfg["blockMACBytes"] << config->blockMACBytes; + cfg["blockMACRandBytes"] << config->blockMACRandBytes; + cfg["uniqueIV"] << config->uniqueIV; + cfg["chainedIV"] << config->chainedNameIV; + cfg["externalIV"] << config->externalIVChaining; + + return cfg.save( configFile ); +} + +bool writeV4Config( const char *configFile, EncFSConfig *config ) +{ + ConfigReader cfg; + + cfg["cipher"] << config->cipherIface; + cfg["keySize"] << config->keySize; + cfg["blockSize"] << config->blockSize; + cfg["keyData"] << config->keyData; + + return cfg.save( configFile ); +} + +bool writeV3Config( const char *configFile, EncFSConfig *config ) +{ + bool ok = true; + + int fd = open( configFile, O_RDWR ); + if(fd >= 0) + { + ::pwrite(fd, config->keyData.data(), config->keyData.length(), 0); + close( fd ); + } else + { + rError(_("Error opening key file %s for write: %s"), configFile, + strerror( errno )); + ok = false; + } + + return ok; +} + +static +Cipher::CipherAlgorithm findCipherAlgorithm(const char *name, + int keySize ) +{ + Cipher::AlgorithmList algorithms = Cipher::GetAlgorithmList(); + Cipher::AlgorithmList::const_iterator it; + for(it = algorithms.begin(); it != algorithms.end(); ++it) + { + if( !strcmp( name, it->name.c_str() ) + && it->keyLength.allowed( keySize )) + { + return *it; + } + } + + Cipher::CipherAlgorithm result; + return result; +} + +static +Cipher::CipherAlgorithm selectCipherAlgorithm() +{ + for(;;) + { + // figure out what cipher they want to use.. + // xgroup(setup) + cout << _("The following cipher algorithms are available:") << "\n"; + Cipher::AlgorithmList algorithms = Cipher::GetAlgorithmList(); + Cipher::AlgorithmList::const_iterator it; + int optNum = 1; + for(it = algorithms.begin(); it != algorithms.end(); ++it, ++optNum) + { + cout << optNum << ". " << it->name + << " : " << gettext(it->description.c_str()) << "\n"; + if(it->keyLength.min() == it->keyLength.max()) + { + // shown after algorithm name and description. + // xgroup(setup) + cout << autosprintf(_(" -- key length %i bits") + , it->keyLength.min()) << "\n"; + } else + { + cout << autosprintf( + // shown after algorithm name and description. + // xgroup(setup) + _(" -- Supports key lengths of %i to %i bits"), + it->keyLength.min(), it->keyLength.max()) << "\n"; + } + + if(it->blockSize.min() == it->blockSize.max()) + { + cout << autosprintf( + // shown after algorithm name and description. + // xgroup(setup) + _(" -- block size %i bytes"), it->blockSize.min()) + << "\n"; + } else + { + cout << autosprintf( + // shown after algorithm name and description. + // xgroup(setup) + _(" -- Supports block sizes of %i to %i bytes"), + it->blockSize.min(), it->blockSize.max()) << "\n"; + } + } + + // xgroup(setup) + cout << "\n" << _("Enter the number corresponding to your choice: "); + char answer[10]; + fgets( answer, sizeof(answer), stdin ); + int cipherNum = atoi( answer ); + cout << "\n"; + + if( cipherNum < 1 || cipherNum > (int)algorithms.size() ) + { + cerr << _("Invalid selection.") << "\n"; + continue; + } + + it = algorithms.begin(); + while(--cipherNum) // numbering starts at 1 + ++it; + + Cipher::CipherAlgorithm alg = *it; + + // xgroup(setup) + cout << autosprintf(_("Selected algorithm \"%s\""), alg.name.c_str()) + << "\n\n"; + + return alg; + } +} + +static +Interface selectNameCoding() +{ + for(;;) + { + // figure out what cipher they want to use.. + // xgroup(setup) + cout << _("The following filename encoding algorithms are available:") + << "\n"; + NameIO::AlgorithmList algorithms = NameIO::GetAlgorithmList(); + NameIO::AlgorithmList::const_iterator it; + int optNum = 1; + for(it = algorithms.begin(); it != algorithms.end(); ++it, ++optNum) + { + cout << optNum << ". " << it->name + << " : " << gettext(it->description.c_str()) << "\n"; + } + + // xgroup(setup) + cout << "\n" << _("Enter the number corresponding to your choice: "); + char answer[10]; + fgets( answer, sizeof(answer), stdin ); + int algNum = atoi( answer ); + cout << "\n"; + + if( algNum < 1 || algNum > (int)algorithms.size() ) + { + cerr << _("Invalid selection.") << "\n"; + continue; + } + + it = algorithms.begin(); + while(--algNum) // numbering starts at 1 + ++it; + + // xgroup(setup) + cout << autosprintf(_("Selected algorithm \"%s\""), it->name.c_str()) + << "\"\n\n"; + + return it->iface; + } +} + +static +int selectKeySize( const Cipher::CipherAlgorithm &alg ) +{ + if(alg.keyLength.min() == alg.keyLength.max()) + { + cout << autosprintf(_("Using key size of %i bits"), + alg.keyLength.min()) << "\n"; + return alg.keyLength.min(); + } + + cout << autosprintf( + // xgroup(setup) + _("Please select a key size in bits. The cipher you have chosen\n" + "supports sizes from %i to %i bits in increments of %i bits.\n" + "For example: "), alg.keyLength.min(), alg.keyLength.max(), + alg.keyLength.inc()) << "\n"; + + int numAvail = (alg.keyLength.max() - alg.keyLength.min()) + / alg.keyLength.inc(); + + if(numAvail < 5) + { + // show them all + for(int i=0; i<=numAvail; ++i) + { + if(i) + cout << ", "; + cout << alg.keyLength.min() + i * alg.keyLength.inc(); + } + } else + { + // partial + for(int i=0; i<3; ++i) + { + if(i) + cout << ", "; + cout << alg.keyLength.min() + i * alg.keyLength.inc(); + } + cout << " ... " << alg.keyLength.max() - alg.keyLength.inc(); + cout << ", " << alg.keyLength.max(); + } + // xgroup(setup) + cout << "\n" << _("Selected key size: "); + + char answer[10]; + fgets( answer, sizeof(answer), stdin ); + int keySize = atoi( answer ); + cout << "\n"; + + keySize = alg.keyLength.closest( keySize ); + + // xgroup(setup) + cout << autosprintf(_("Using key size of %i bits"), keySize) << "\n\n"; + + return keySize; +} + +static +int selectBlockSize( const Cipher::CipherAlgorithm &alg ) +{ + if(alg.blockSize.min() == alg.blockSize.max()) + { + cout << autosprintf( + // xgroup(setup) + _("Using filesystem block size of %i bytes"), + alg.blockSize.min()) << "\n"; + return alg.blockSize.min(); + } + + cout << autosprintf( + // xgroup(setup) + _("Select a block size in bytes. The cipher you have chosen\n" + "supports sizes from %i to %i bytes in increments of %i.\n" + "Or just hit enter for the default (%i bytes)\n"), + alg.blockSize.min(), alg.blockSize.max(), alg.blockSize.inc(), + DefaultBlockSize); + + // xgroup(setup) + cout << "\n" << _("filesystem block size: "); + + int blockSize = DefaultBlockSize; + char answer[10]; + fgets( answer, sizeof(answer), stdin ); + cout << "\n"; + + if( atoi( answer ) >= alg.blockSize.min() ) + blockSize = atoi( answer ); + + blockSize = alg.blockSize.closest( blockSize ); + + // xgroup(setup) + cout << autosprintf(_("Using filesystem block size of %i bytes"), + blockSize) << "\n\n"; + + return blockSize; +} + +static +void selectBlockMAC(int *macBytes, int *macRandBytes) +{ + // xgroup(setup) + cout << _("Enable block authentication code headers\n" + "on every block in a file? This adds about 12 bytes per block\n" + "to the storage requirements for a file, and significantly affects\n" + "performance but it also means [almost] any modifications or errors\n" + "within a block will be caught and will cause a read error.\n" + "The default here is No. \n" + "Any response that does not begin with 'y' will mean No: "); + + char answer[10]; + fgets( answer, sizeof(answer), stdin ); + cout << "\n"; + + if(tolower(answer[0]) == 'y') + { + *macBytes = 8; + + // xgroup(setup) + cout << _("Add random bytes to each block header?\n" + "This adds a performance penalty, but ensures that blocks\n" + "have different authentication codes. Note that you can\n" + "have the same benefits by enabling per-file initialization\n" + "vectors, which does not come with as great of performance\n" + "penalty. \n" + "Select a number of bytes, from 0 (no random bytes) to 8: "); + + int randSize = 0; + fgets( answer, sizeof(answer), stdin ); + cout << "\n"; + + randSize = atoi( answer ); + if(randSize < 0) + randSize = 0; + if(randSize > 8) + randSize = 8; + + *macRandBytes = randSize; + } else + { + *macBytes = 0; + *macRandBytes = 0; + } +} + +static +bool selectUniqueIV() +{ + // xgroup(setup) + cout << _("Enable per-file initialization vectors?\n" + "This adds about 8 bytes per file to the storage requirements.\n" + "It should not affect performance except possibly with applications\n" + "which rely on block-aligned file io for performance.\n" + "The default here is Yes. \n" + "Any response that does not begin with 'n' will mean Yes: "); + + char answer[10]; + fgets( answer, sizeof(answer), stdin ); + cout << "\n"; + + if(tolower(answer[0]) == 'n') + return false; + else + return true; +} + +static +bool selectChainedIV() +{ + // xgroup(setup) + cout << _("Enable filename initialization vector chaining?\n" + "This makes filename encoding dependent on the complete path, \n" + "rather then encoding each path element individually. \n" + "This is normally desireable, therefor the default is Yes. \n" + "Any response that does not begin with 'n' will mean Yes: "); + + char answer[10]; + fgets( answer, sizeof(answer), stdin ); + cout << "\n"; + + if(tolower(answer[0]) == 'n') + return false; + else + return true; +} + +static +bool selectExternalChainedIV() +{ + // xgroup(setup) + cout << _("Enable filename to IV header chaining?\n" + "This makes file data encoding dependent on the complete file path.\n" + "If a file is renamed, it will not decode sucessfully unless it\n" + "was renamed by encfs with the proper key.\n" + "If this option is enabled, then hard links will not be supported\n" + "in the filesystem.\n" + "The default is No. \n" + "Any response that does not begin with 'y' will mean No: "); + + char answer[10]; + fgets( answer, sizeof(answer), stdin ); + cout << "\n"; + + if(tolower(answer[0]) == 'y') + return true; + else + return false; +} + +RootPtr createV5Config( EncFS_Context *ctx, const std::string &rootDir, + bool enableIdleTracking, bool forceDecode, + const std::string &passwordProgram, + bool useStdin, bool reverseEncryption ) +{ + RootPtr rootInfo; + + // creating new volume key.. should check that is what the user is + // expecting... + // xgroup(setup) + cout << _("Creating new encrypted volume.") << endl; + + // xgroup(setup) + cout << _("Please choose from one of the following options:\n" + " enter \"x\" for expert configuration mode,\n" + " enter \"p\" for pre-configured paranoia mode,\n" + " anything else, or an empty line will select standard mode.\n" + "?> "); + + char answer[10] = {0}; + fgets( answer, sizeof(answer), stdin ); + cout << "\n"; + + int keySize = 0; + int blockSize = 0; + Cipher::CipherAlgorithm alg; + Interface nameIOIface; + int blockMACBytes = 0; + int blockMACRandBytes = 0; + bool uniqueIV = false; + bool chainedIV = false; + bool externalIV = false; + + if (reverseEncryption) + { + uniqueIV = false; + chainedIV = false; + externalIV = false; + blockMACBytes = 0; + blockMACRandBytes = 0; + } + + if(answer[0] == 'p') + { + if (reverseEncryption) + { + rError(_("Paranoia configuration not supported for --reverse")); + return rootInfo; + } + + // xgroup(setup) + cout << _("Paranoia configuration selected.") << "\n"; + // look for AES with 256 bit key.. + // Use block filename encryption mode. + // Enable per-block HMAC headers at substantial performance penalty.. + // Enable per-file initialization vector headers. + // Enable filename initialization vector chaning + keySize = 256; + blockSize = DefaultBlockSize; + alg = findCipherAlgorithm("AES", keySize); + nameIOIface = BlockNameIO::CurrentInterface(); + blockMACBytes = 8; + blockMACRandBytes = 0; // using uniqueIV, so this isn't necessary + uniqueIV = true; + chainedIV = true; + externalIV = true; + } else + if(answer[0] != 'x') + { + // xgroup(setup) + cout << _("Standard configuration selected.") << "\n"; + // look for blowfish with 160 bit key.. + // block name encoding is now standard as well.. + // per-file initialization vectors are now standard, as they shouldn't + // have any significant performance penalty. + keySize = 160; + blockSize = DefaultBlockSize; + alg = findCipherAlgorithm("Blowfish", keySize); + blockMACBytes = 0; + externalIV = false; + nameIOIface = BlockNameIO::CurrentInterface(); + + if (reverseEncryption) + { + cout << _("--reverse specified, not using unique/chained IV") + << "\n"; + } else + { + uniqueIV = true; + chainedIV = true; + } + } + + if(answer[0] == 'x' || alg.name.empty()) + { + if(answer[0] != 'x') + { + // xgroup(setup) + cout << _("Sorry, unable to locate cipher for predefined " + "configuration...\n" + "Falling through to Manual configuration mode."); + } else + { + // xgroup(setup) + cout << _("Manual configuration mode selected."); + } + cout << endl; + + // query user for settings.. + alg = selectCipherAlgorithm(); + keySize = selectKeySize( alg ); + blockSize = selectBlockSize( alg ); + nameIOIface = selectNameCoding(); + if (reverseEncryption) + { + cout << _("--reverse specified, not using unique/chained IV") << "\n"; + } else + { + chainedIV = selectChainedIV(); + uniqueIV = selectUniqueIV(); + if(chainedIV && uniqueIV) + externalIV = selectExternalChainedIV(); + else + { + // xgroup(setup) + cout << _("External chained IV disabled, as both 'IV chaining'\n" + "and 'unique IV' features are required for this option.") + << endl; + externalIV = false; + } + selectBlockMAC(&blockMACBytes, &blockMACRandBytes); + } + } + + shared_ptr cipher = Cipher::New( alg.name, keySize ); + if(!cipher) + { + rError(_("Unable to instanciate cipher %s, key size %i, block size %i"), + alg.name.c_str(), keySize, blockSize); + return rootInfo; + } else + { + rDebug("Using cipher %s, key size %i, block size %i", + alg.name.c_str(), keySize, blockSize); + } + + EncFSConfig config; + + config.cipherIface = cipher->interface(); + config.keySize = keySize; + config.blockSize = blockSize; + config.nameIface = nameIOIface; + config.creator = "EncFS " VERSION; + config.subVersion = V5SubVersion; + config.blockMACBytes = blockMACBytes; + config.blockMACRandBytes = blockMACRandBytes; + config.uniqueIV = uniqueIV; + config.chainedNameIV = chainedIV; + config.externalIVChaining = externalIV; + + cout << "\n"; + // xgroup(setup) + cout << _("Configuration finished. The filesystem to be created has\n" + "the following properties:") << endl; + showFSInfo( config ); + + if( config.externalIVChaining ) + { + cout << + _("-------------------------- WARNING --------------------------\n") + << + _("The external initialization-vector chaining option has been\n" + "enabled. This option disables the use of hard links on the\n" + "filesystem. Without hard links, some programs may not work.\n" + "The programs 'mutt' and 'procmail' are known to fail. For\n" + "more information, please see the encfs mailing list.\n" + "If you would like to choose another configuration setting,\n" + "please press CTRL-C now to abort and start over.") << endl; + cout << endl; + } + + // xgroup(setup) + cout << _("Now you will need to enter a password for your filesystem.\n" + "You will need to remember this password, as there is absolutely\n" + "no recovery mechanism. However, the password can be changed\n" + "later using encfsctl.\n\n"); + + int encodedKeySize = cipher->encodedKeySize(); + unsigned char *encodedKey = new unsigned char[ encodedKeySize ]; + + CipherKey volumeKey = cipher->newRandomKey(); + + // get user key and use it to encode volume key + CipherKey userKey; + rDebug( "useStdin: %i", useStdin ); + if(useStdin) + userKey = getUserKey( cipher, useStdin ); + else if(passwordProgram.empty()) + userKey = getNewUserKey( cipher ); + else + userKey = getUserKey( passwordProgram, cipher, rootDir ); + + cipher->writeKey( volumeKey, encodedKey, userKey ); + userKey.reset(); + + config.keyData.assign( (char*)encodedKey, encodedKeySize ); + delete[] encodedKey; + + if(!volumeKey) + { + rWarning(_("Failure generating new volume key! " + "Please report this error.")); + return rootInfo; + } + + if(!saveConfig( Config_V5, rootDir, &config )) + return rootInfo; + + // fill in config struct + shared_ptr nameCoder = NameIO::New( config.nameIface, + cipher, volumeKey ); + if(!nameCoder) + { + rWarning(_("Name coding interface not supported")); + cout << _("The filename encoding interface requested is not available") + << endl; + return rootInfo; + } + + nameCoder->setChainedNameIV( config.chainedNameIV ); + nameCoder->setReverseEncryption( reverseEncryption ); + + shared_ptr dirNodeConfig (new DirNode::Config); + dirNodeConfig->cipher = cipher; + dirNodeConfig->key = volumeKey; + dirNodeConfig->nameCoding = nameCoder; + dirNodeConfig->fsSubVersion = config.subVersion; + dirNodeConfig->blockSize = blockSize; + dirNodeConfig->inactivityTimer = enableIdleTracking; + dirNodeConfig->blockMACBytes = config.blockMACBytes; + dirNodeConfig->blockMACRandBytes = config.blockMACRandBytes; + dirNodeConfig->uniqueIV = config.uniqueIV; + dirNodeConfig->externalIVChaining = config.externalIVChaining; + dirNodeConfig->forceDecode = forceDecode; + dirNodeConfig->reverseEncryption = reverseEncryption; + + rootInfo = RootPtr( new EncFS_Root ); + rootInfo->cipher = cipher; + rootInfo->volumeKey = volumeKey; + rootInfo->root = shared_ptr( + new DirNode( ctx, rootDir, dirNodeConfig )); + + return rootInfo; +} + +void showFSInfo( const EncFSConfig &config ) +{ + shared_ptr cipher = Cipher::New( config.cipherIface, -1 ); + { + cout << autosprintf( + // xgroup(diag) + _("Filesystem cipher: \"%s\", version %i:%i:%i"), + config.cipherIface.name().c_str(), config.cipherIface.current(), + config.cipherIface.revision(), config.cipherIface.age()); + // check if we support this interface.. + if(!cipher) + cout << _(" (NOT supported)\n"); + else + { + // if we're using a newer interface, show the version number + if( config.cipherIface != cipher->interface() ) + { + Interface iface = cipher->interface(); + // xgroup(diag) + cout << autosprintf(_(" (using %i:%i:%i)\n"), + iface.current(), iface.revision(), iface.age()); + } else + cout << "\n"; + } + } + { + // xgroup(diag) + cout << autosprintf(_("Filename encoding: \"%s\", version %i:%i:%i"), + config.nameIface.name().c_str(), config.nameIface.current(), + config.nameIface.revision(), config.nameIface.age()); + + // check if we support the filename encoding interface.. + shared_ptr nameCoder = NameIO::New( config.nameIface, + cipher, CipherKey() ); + if(!nameCoder) + { + // xgroup(diag) + cout << _(" (NOT supported)\n"); + } else + { + // if we're using a newer interface, show the version number + if( config.nameIface != nameCoder->interface() ) + { + Interface iface = nameCoder->interface(); + cout << autosprintf(_(" (using %i:%i:%i)\n"), + iface.current(), iface.revision(), iface.age()); + } else + cout << "\n"; + } + } + { + cout << autosprintf(_("Key Size: %i bits"), config.keySize); + cipher = Cipher::New( config.cipherIface, config.keySize ); + if(!cipher) + { + // xgroup(diag) + cout << _(" (NOT supported)\n"); + } else + cout << "\n"; + } + if(config.blockMACBytes) + { + if(config.subVersion < 20040813) + { + cout << autosprintf( + // xgroup(diag) + _("Block Size: %i bytes + %i byte MAC header"), + config.blockSize, + config.blockMACBytes + config.blockMACRandBytes) << endl; + } else + { + // new version stores the header as part of that block size.. + cout << autosprintf( + // xgroup(diag) + _("Block Size: %i bytes, including %i byte MAC header"), + config.blockSize, + config.blockMACBytes + config.blockMACRandBytes) << endl; + } + } else + { + // xgroup(diag) + cout << autosprintf(_("Block Size: %i bytes"), config.blockSize); + cout << "\n"; + } + + if(config.uniqueIV) + { + // xgroup(diag) + cout << _("Each file contains 8 byte header with unique IV data.\n"); + } + if(config.chainedNameIV) + { + // xgroup(diag) + cout << _("Filenames encoded using IV chaining mode.\n"); + } + if(config.externalIVChaining) + { + // xgroup(diag) + cout << _("File data IV is chained to filename IV.\n"); + } + cout << "\n"; +} + +CipherKey getUserKey( const shared_ptr &cipher, bool useStdin ) +{ + const int MaxPassBuf = 1024; + char passBuf[MaxPassBuf]; + char *res; + + if( useStdin ) + { + res = fgets( passBuf, sizeof(passBuf), stdin ); + } else + { + // xgroup(common) + res = readpassphrase( _("EncFS Password: "), + passBuf, sizeof(passBuf), RPP_ECHO_OFF ); + } + + CipherKey userKey; + if(!res) + cerr << _("Zero length password not allowed\n"); + else + userKey = cipher->newKey( passBuf, strlen(passBuf) ); + + memset( passBuf, 0, sizeof(passBuf) ); + + return userKey; +} + +std::string readPassword( int FD ) +{ + char buffer[2048]; + string result; + + while(1) + { + ssize_t rdSize = recv(FD, buffer, sizeof(buffer), 0); + + if(rdSize > 0) + { + result.append( buffer, rdSize ); + memset(buffer, 0, sizeof(buffer)); + } else + break; + } + + // chop off trailing "\n" if present.. + // This is done so that we can use standard programs like ssh-askpass + // without modification, as it returns trailing newline.. + if(!result.empty() && result[ result.length()-1 ] == '\n' ) + result.resize( result.length() -1 ); + + return result; +} + +CipherKey getUserKey( const std::string &passProg, + const shared_ptr &cipher, const std::string &rootDir ) +{ + // have a child process run the command and get the result back to us. + int fds[2], pid; + int res; + CipherKey result; + + res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds); + if(res == -1) + { + perror(_("Internal error: socketpair() failed")); + return result; + } + rDebug("getUserKey: fds = %i, %i", fds[0], fds[1]); + + pid = fork(); + if(pid == -1) + { + perror(_("Internal error: fork() failed")); + close(fds[0]); + close(fds[1]); + return result; + } + + if(pid == 0) + { + const char *argv[4]; + argv[0] = "/bin/sh"; + argv[1] = "-c"; + argv[2] = passProg.c_str(); + argv[3] = 0; + + // child process.. run the command and send output to fds[0] + close(fds[1]); // we don't use the other half.. + + // make a copy of stdout and stderr descriptors, and set an environment + // variable telling where to find them, in case a child wants it.. + int stdOutCopy = dup( STDOUT_FILENO ); + int stdErrCopy = dup( STDERR_FILENO ); + // replace STDOUT with our socket, which we'll used to receive the + // password.. + dup2( fds[0], STDOUT_FILENO ); + + // ensure that STDOUT_FILENO and stdout/stderr are not closed on exec.. + fcntl(STDOUT_FILENO, F_SETFD, 0); // don't close on exec.. + fcntl(stdOutCopy, F_SETFD, 0); + fcntl(stdErrCopy, F_SETFD, 0); + + char tmpBuf[8]; + + setenv(ENCFS_ENV_ROOTDIR, rootDir.c_str(), 1); + + snprintf(tmpBuf, sizeof(tmpBuf)-1, "%i", stdOutCopy); + setenv(ENCFS_ENV_STDOUT, tmpBuf, 1); + + snprintf(tmpBuf, sizeof(tmpBuf)-1, "%i", stdErrCopy); + setenv(ENCFS_ENV_STDERR, tmpBuf, 1); + + execvp( argv[0], (char * const *)argv ); // returns only on error.. + + perror(_("Internal error: failed to exec program")); + exit(1); + } + + close(fds[0]); + string password = readPassword(fds[1]); + close(fds[1]); + + waitpid(pid, NULL, 0); + + // convert to key.. + result = cipher->newKey( password.c_str(), password.length() ); + + // clear buffer.. + password.assign( password.length(), '\0' ); + + return result; +} + +CipherKey getNewUserKey( const shared_ptr &cipher ) +{ + const int MaxPassBuf = 64; + CipherKey userKey; + char passBuf[MaxPassBuf]; + char passBuf2[MaxPassBuf]; + + do + { + // xgroup(common) + char *res1 = readpassphrase(_("New Encfs Password: "), passBuf, + sizeof(passBuf)-1, RPP_ECHO_OFF); + // xgroup(common) + char *res2 = readpassphrase(_("Verify Encfs Password: "), passBuf2, + sizeof(passBuf2)-1, RPP_ECHO_OFF); + + if(res1 && res2 && !strcmp(passBuf, passBuf2)) + userKey = cipher->newKey( passBuf, strlen(passBuf) ); + else + { + // xgroup(common) -- probably not common, but group with the others + cerr << _("Passwords did not match, please try again\n"); + } + + memset( passBuf, 0, sizeof(passBuf) ); + memset( passBuf2, 0, sizeof(passBuf2) ); + } while( !userKey ); + + return userKey; +} + +RootPtr initFS( EncFS_Context *ctx, const shared_ptr &opts ) +{ + RootPtr rootInfo; + EncFSConfig config; + + if(readConfig( opts->rootDir, &config ) != Config_None) + { + if(opts->reverseEncryption) + { + if (config.blockMACBytes != 0 || config.blockMACRandBytes != 0 + || config.uniqueIV == true || config.externalIVChaining == true + || config.chainedNameIV == true ) + { + cout << _("The configuration loaded is not compatible with --reverse\n"); + return rootInfo; + } + } + +// first, instanciate the cipher. + shared_ptr cipher = + Cipher::New( config.cipherIface, config.keySize ); + if(!cipher) + { + rError(_("Unable to find cipher %s, version %i:%i:%i"), + config.cipherIface.name().c_str(), + config.cipherIface.current(), + config.cipherIface.revision(), + config.cipherIface.age()); + // xgroup(diag) + cout << _("The requested cipher interface is not available\n"); + return rootInfo; + } + + // get user key + CipherKey userKey; + rDebug( "useStdin: %i", opts->useStdin ); + if(opts->passwordProgram.empty()) + userKey = getUserKey( cipher, opts->useStdin ); + else + userKey = getUserKey( opts->passwordProgram, + cipher, opts->rootDir ); + + if(!userKey) + return rootInfo; + + // no funny stuff + rDebug("configuration key size = %i", (int)config.keyData.length()); + rDebug("cipher key size = %i", cipher->encodedKeySize()); + rAssert( (int)config.keyData.length() == cipher->encodedKeySize() ); + // decode volume key.. + CipherKey volumeKey = cipher->readKey( + (unsigned char*)config.keyData.data(), userKey, opts->checkKey); + userKey.reset(); + + if(!volumeKey) + { + // xgroup(diag) + cout << _("Error decoding volume key, password incorrect\n"); + return rootInfo; + } + + shared_ptr nameCoder = NameIO::New( config.nameIface, + cipher, volumeKey ); + if(!nameCoder) + { + rError(_("Unable to find nameio interface %s, version %i:%i:%i"), + config.nameIface.name().c_str(), + config.nameIface.current(), + config.nameIface.revision(), + config.nameIface.age()); + // xgroup(diag) + cout << _("The requested filename coding interface is " + "not available\n"); + return rootInfo; + } + + nameCoder->setChainedNameIV( config.chainedNameIV ); + nameCoder->setReverseEncryption( opts->reverseEncryption ); + + shared_ptr dirNodeConfig (new DirNode::Config); + dirNodeConfig->cipher = cipher; + dirNodeConfig->key = volumeKey; + dirNodeConfig->nameCoding = nameCoder; + dirNodeConfig->fsSubVersion = config.subVersion; + dirNodeConfig->blockSize = config.blockSize; + dirNodeConfig->inactivityTimer = opts->idleTracking; + dirNodeConfig->blockMACBytes = config.blockMACBytes; + dirNodeConfig->blockMACRandBytes = config.blockMACRandBytes; + dirNodeConfig->uniqueIV = config.uniqueIV; + dirNodeConfig->externalIVChaining = config.externalIVChaining; + dirNodeConfig->forceDecode = opts->forceDecode; + dirNodeConfig->reverseEncryption = opts->reverseEncryption; + + rootInfo = RootPtr( new EncFS_Root ); + rootInfo->cipher = cipher; + rootInfo->volumeKey = volumeKey; + rootInfo->root = shared_ptr( + new DirNode( ctx, opts->rootDir, dirNodeConfig )); + } else + { + if(opts->createIfNotFound) + { + // creating a new encrypted filesystem + rootInfo = createV5Config( ctx, opts->rootDir, opts->idleTracking, + opts->forceDecode, opts->passwordProgram, opts->useStdin, + opts->reverseEncryption ); + } + } + + return rootInfo; +} + +int remountFS(EncFS_Context *ctx) +{ + rDebug("Attempting to reinitialize filesystem"); + + RootPtr rootInfo = initFS( ctx, ctx->opts ); + if(rootInfo) + { + ctx->setRoot(rootInfo->root); + return 0; + } else + { + rInfo(_("Remount failed")); + return -EACCES; + } +} + diff --git a/encfs/FileUtils.h b/encfs/FileUtils.h new file mode 100644 index 0000000..e2609a2 --- /dev/null +++ b/encfs/FileUtils.h @@ -0,0 +1,161 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This program is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef _FileUtils_incl_ +#define _FileUtils_incl_ + +#include "encfs.h" +#include "Interface.h" +#include "DirNode.h" +#include "CipherKey.h" + +// true if the path points to an existing node (of any type) +bool fileExists( const char *fileName ); +// true if path is a directory +bool isDirectory( const char *fileName ); +// true if starts with '/' +bool isAbsolutePath( const char *fileName ); +// pointer to just after the last '/' +const char *lastPathElement( const char *name ); + +std::string parentDirectory( const std::string &path ); + +// ask the user for permission to create the directory. If they say ok, then +// do it and return true. +bool userAllowMkdir( const char *dirPath, mode_t mode ); + +struct EncFSConfig +{ + std::string creator; + int subVersion; + + // interface of cipher + rel::Interface cipherIface; + // interface used for file name coding + rel::Interface nameIface; + int keySize; // reported in bits + int blockSize; // reported in bytes + std::string keyData; + + int blockMACBytes; // MAC headers on blocks.. + int blockMACRandBytes; // number of random bytes in the block header + + bool uniqueIV; // per-file Initialization Vector + bool externalIVChaining; // IV seeding by filename IV chaining + + bool chainedNameIV; // filename IV chaining +}; + +enum ConfigType +{ + Config_None = 0, + Config_Prehistoric, + Config_V3, + Config_V4, + Config_V5 +}; + +class Cipher; + +struct EncFS_Root +{ + boost::shared_ptr cipher; + CipherKey volumeKey; + boost::shared_ptr root; + + EncFS_Root(); + ~EncFS_Root(); +}; + +typedef boost::shared_ptr RootPtr; + +/* + Read existing config file. Looks for any supported configuration version. +*/ +ConfigType readConfig( const std::string &rootDir, EncFSConfig *config ); + +/* + Save the configuration. Saves back as the same configuration type as was + read from. +*/ +bool saveConfig( ConfigType type, const std::string &rootdir, + EncFSConfig *config ); + + +struct EncFS_Opts +{ + std::string rootDir; + bool createIfNotFound; // create filesystem if not found + bool idleTracking; // turn on idle monitoring of filesystem + bool mountOnDemand; // mounting on-demand + + bool checkKey; // check crypto key decoding + bool forceDecode; // force decode on MAC block failures + + std::string passwordProgram; // path to password program (or empty) + bool useStdin; // read password from stdin rather then prompting + + bool ownerCreate; // set owner of new files to caller + + bool reverseEncryption; // Reverse encryption + EncFS_Opts() + { + createIfNotFound = true; + idleTracking = false; + mountOnDemand = false; + checkKey = true; + forceDecode = false; + useStdin = false; + ownerCreate = false; + reverseEncryption = false; + } +}; + +class EncFS_Context; + +RootPtr initFS( EncFS_Context *ctx, const shared_ptr &opts ); + +RootPtr createV5Config( EncFS_Context *ctx, const std::string &rootDir, + bool enableIdleTracking, + bool forceDecode, + const std::string &passwordProgram, bool reverseEncryption ); + + +void showFSInfo( const EncFSConfig &config ); + +// Read specifically a version 4 configuration file. +bool readV3Config( const char *configFile, EncFSConfig *config, + struct ConfigInfo *); +bool writeV3Config( const char *configFile, EncFSConfig *config); + +bool readV4Config( const char *configFile, EncFSConfig *config, + struct ConfigInfo *); +bool writeV4Config( const char *configFile, EncFSConfig *config); + +bool readV5Config( const char *configFile, EncFSConfig *config, + struct ConfigInfo *); +bool writeV5Config( const char *configFile, EncFSConfig *config); + + + +CipherKey getUserKey(const boost::shared_ptr &cipher, bool useStdin); +CipherKey getUserKey(const std::string &passwordProgram, + const boost::shared_ptr &cipher, + const std::string &rootDir ); +CipherKey getNewUserKey(const boost::shared_ptr &cipher); + +#endif diff --git a/encfs/Interface.cpp b/encfs/Interface.cpp new file mode 100644 index 0000000..01ecb1c --- /dev/null +++ b/encfs/Interface.cpp @@ -0,0 +1,226 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This program is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include "Interface.h" + +#include "ConfigVar.h" + +#include +#include + +using namespace rel; +using namespace rlog; + +static RLogChannel * Info = DEF_CHANNEL( "info/iface", Log_Info ); + +Interface::Interface(const char *name_, int Current, int Revision, int Age) + : _name( name_ ) + , _current( Current ) + , _revision( Revision ) + , _age( Age ) +{ +} + +Interface::Interface(const std::string &name_, int Current, + int Revision, int Age) + : _name( name_ ) + , _current( Current ) + , _revision( Revision ) + , _age( Age ) +{ +} + +Interface::Interface(const Interface &src) + : _name( src._name ) + , _current( src._current ) + , _revision( src._revision ) + , _age( src._age ) +{ +} + +Interface::Interface() + : _current( 0 ) + , _revision( 0 ) + , _age( 0 ) +{ +} + +Interface &Interface::operator = (const Interface &src) +{ + _name = src._name; + _current = src._current; + _revision = src._revision; + _age = src._age; + return *this; +} + +const std::string & Interface::name() const +{ + return _name; +} + +std::string & Interface::name() +{ + return _name; +} + +int Interface::current() const +{ + return _current; +} + +int &Interface::current() +{ + return _current; +} + +int Interface::revision() const +{ + return _revision; +} + +int &Interface::revision() +{ + return _revision; +} + +int Interface::age() const +{ + return _age; +} + +int &Interface::age() +{ + return _age; +} + +bool operator == (const Interface &A, const Interface &B) +{ + return ( A.name() == B.name() + && A.current() == B.current() + && A.revision() == B.revision() + && A.age() == B.age() ); +} + +bool operator != (const Interface &A, const Interface &B) +{ + return ( A.name() != B.name() + || A.current() != B.current() + || A.revision() != B.revision() + || A.age() != B.age() ); +} + +// zero branch method of getting comparison sign.. +// tricky.. makes assumptions +#if 0 +static int sign( int a, int b ) +{ + unsigned int ab = ((unsigned int)(a - b)) >> 31; + unsigned int ba = ((unsigned int)(b - a)) >> 31; + + return 1 + ba - ab; +} +#else +// simple, easy to check, unlikely to break due to unforseen events.. +static int sign( int a, int b ) +{ + if(a < b) + return 0; + else if(a == b) + return 1; + else + return 2; +} +#endif + +static int diffSum( const Interface &A, const Interface &B ) +{ + int cS = sign( A.current() , B.current() ); + int aS = sign( A.age(), B.age() ); + int rS = sign( A.revision(), B.revision() ); + + return (cS * 3 + aS) * 3 + rS; +} + +const int EqualVersion = (1 * 3 + 1) * 3 + 1; + +bool Interface::implements(const Interface &B) const +{ + rLog(Info, "checking if %s(%i:%i:%i) implements %s(%i:%i:%i)", + name().c_str(), current(), revision(), age(), + B.name().c_str(), B.current(), B.revision(), B.age()); + + if( name() != B.name() ) + return false; + + int currentDiff = current() - B.current(); + return ( currentDiff >= 0 && currentDiff <= age() ); +} + + +bool operator < (const Interface &A, const Interface &B) +{ + if( A.name() == B.name() ) + { + return ( diffSum(A,B) < EqualVersion ); + } else + return A.name() < B.name(); +} + +bool operator > (const Interface &A, const Interface &B) +{ + if( A.name() == B.name() ) + { + return ( diffSum(A,B) > EqualVersion ); + } else + return A.name() < B.name(); +} + +bool operator <= (const Interface &A, const Interface &B) +{ + if( A.name() == B.name() ) + { + return ( diffSum(A,B) <= EqualVersion ); + } else + return A.name() < B.name(); +} + +bool operator >= (const Interface &A, const Interface &B) +{ + if( A.name() == B.name() ) + { + return ( diffSum(A,B) >= EqualVersion ); + } else + return A.name() < B.name(); +} + + +ConfigVar & operator << (ConfigVar &dst, const rel::Interface &iface) +{ + dst << iface.name() << iface.current() << iface.revision() << iface.age(); + return dst; +} + +const ConfigVar & operator >> (const ConfigVar &src, Interface &iface) +{ + src >> iface.name(); + src >> iface.current(); + src >> iface.revision(); + src >> iface.age(); + return src; +} + diff --git a/encfs/Interface.h b/encfs/Interface.h new file mode 100644 index 0000000..b74e3f7 --- /dev/null +++ b/encfs/Interface.h @@ -0,0 +1,84 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This program is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef _Interface_incl_ +#define _Interface_incl_ + +#include + +class ConfigVar; + +// part of REL library.. +namespace rel +{ + + class Interface + { + public: + + /*! + Version numbers as described by libtool: info://libtool/versioning + Current - the most recent interface api that is implemented. + Revision - the implementation number of the current interface. + Age - the difference between the newest and oldest interfaces that + are implemented. + */ + Interface( const char *name, int Current, int Revision, int Age ); + Interface( const std::string &name, int Current, int Revision, int Age); + Interface(const Interface &src); + Interface(); + + // check if we implement the interface described by B. + // Note that A.implements(B) is not the same as B.implements(A) + // This checks the current() version and age() against B.current() for + // compatibility. Even if A.implements(B) is true, B > A may also be + // true, meaning B is a newer revision of the interface then A. + bool implements( const Interface &dst ) const; + + const std::string &name() const; + int current() const; + int revision() const; + int age() const; + + std::string &name(); + int ¤t(); + int &revision(); + int &age(); + + Interface &operator = ( const Interface &src ); + + private: + std::string _name; + int _current; + int _revision; + int _age; + }; + +} + +ConfigVar & operator << (ConfigVar &, const rel::Interface &); +const ConfigVar & operator >> (const ConfigVar &, rel::Interface &); + +bool operator < (const rel::Interface &A, const rel::Interface &B); +bool operator > (const rel::Interface &A, const rel::Interface &B); +bool operator <= (const rel::Interface &A, const rel::Interface &B); +bool operator >= (const rel::Interface &A, const rel::Interface &B); +bool operator == (const rel::Interface &A, const rel::Interface &B); +bool operator != (const rel::Interface &A, const rel::Interface &B); + +#endif + diff --git a/encfs/MACFileIO.cpp b/encfs/MACFileIO.cpp new file mode 100644 index 0000000..09abfa0 --- /dev/null +++ b/encfs/MACFileIO.cpp @@ -0,0 +1,270 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + */ + +#include "MACFileIO.h" + +#include "MemoryPool.h" + +#include +#include +#include + +#include + +#include "i18n.h" + +using namespace rlog; +using namespace rel; +using namespace std; + +static RLogChannel *Info = DEF_CHANNEL("info/MACFileIO", Log_Info); +// +// Version 1.0 worked on blocks of size (blockSize + headerSize). +// That is, it took [blockSize] worth of user data and added headers. +// Version 2.0 takes [blockSize - headerSize] worth of user data and writes +// [blockSize] bytes. That way the size going into the crypto engine is +// valid from what was selected based on the crypto module allowed ranges! +// +// The information about MACFileIO currently does not make its way into the +// configuration file, so there is no easy way to make this backward +// compatible, except at a high level by checking a revision number for the +// filesystem... +// +static rel::Interface MACFileIO_iface("FileIO/MAC", 2, 0, 0); + +MACFileIO::MACFileIO( const shared_ptr &_base, + const shared_ptr &_cipher, + const CipherKey &_key, int fsBlockSize, + int _macBytes, int _randBytes, + bool warnOnlyMode ) + : BlockFileIO( fsBlockSize - _macBytes - _randBytes ) + , base( _base ) + , cipher( _cipher ) + , key( _key ) + , macBytes( _macBytes ) + , randBytes( _randBytes ) + , warnOnly( warnOnlyMode ) +{ + rAssert( macBytes > 0 && macBytes <= 8 ); + rAssert( randBytes >= 0 ); + rLog(Info, "fs block size = %i, macBytes = %i, randBytes = %i", + fsBlockSize, macBytes, randBytes); +} + +MACFileIO::~MACFileIO() +{ +} + +rel::Interface MACFileIO::interface() const +{ + return MACFileIO_iface; +} + +int MACFileIO::open( int flags ) +{ + return base->open( flags ); +} + +void MACFileIO::setFileName( const char *fileName ) +{ + base->setFileName( fileName ); +} + +const char *MACFileIO::getFileName() const +{ + return base->getFileName(); +} + +bool MACFileIO::setIV( uint64_t iv ) +{ + return base->setIV( iv ); +} + +inline static off_t roundUpDivide( off_t numerator, int denominator ) +{ + // integer arithmetic always rounds down, so we can round up by adding + // enough so that any value other then a multiple of denominator gets + // rouned to the next highest value. + return ( numerator + denominator - 1 ) / denominator; +} + +// Convert from a location in the raw file to a location when MAC headers are +// interleved with the data. +// So, if the filesystem stores/encrypts [blockSize] bytes per block, then +// [blockSize - headerSize] of those bytes will contain user-supplied data, +// and the rest ([headerSize]) will contain the MAC header for this block. +// Example, offset points to second block (of user-data) +// offset = blockSize - headerSize +// ... blockNum = 1 +// ... partialBlock = 0 +// ... adjLoc = 1 * blockSize +static off_t locWithHeader( off_t offset, int blockSize, int headerSize ) +{ + off_t blockNum = roundUpDivide( offset , blockSize - headerSize ); + return offset + blockNum * headerSize; +} + +// convert from a given location in the stream containing headers, and return a +// location in the user-data stream (which doesn't contain MAC headers).. +// The output value will always be less then the input value, because the +// headers are stored at the beginning of the block, so even the first data is +// offset by the size of the header. +static off_t locWithoutHeader( off_t offset, int blockSize, int headerSize ) +{ + off_t blockNum = roundUpDivide( offset , blockSize ); + return offset - blockNum * headerSize; +} + +int MACFileIO::getAttr( struct stat *stbuf ) const +{ + int res = base->getAttr( stbuf ); + + if(res == 0 && S_ISREG(stbuf->st_mode)) + { + // have to adjust size field.. + int headerSize = macBytes + randBytes; + int bs = blockSize() + headerSize; + stbuf->st_size = locWithoutHeader( stbuf->st_size, bs, headerSize ); + } + + return res; +} + +off_t MACFileIO::getSize() const +{ + // adjust the size to hide the header overhead we tack on.. + int headerSize = macBytes + randBytes; + int bs = blockSize() + headerSize; + + off_t size = base->getSize(); + if(size > 0) + size = locWithoutHeader( size, bs, headerSize ); + + return size; +} + +ssize_t MACFileIO::readOneBlock( const IORequest &req ) const +{ + int headerSize = macBytes + randBytes; + + int bs = blockSize() + headerSize; + + MemBlock mb = MemoryPool::allocate( bs ); + + IORequest tmp; + tmp.offset = locWithHeader( req.offset, bs, headerSize ); + tmp.data = mb.data; + tmp.dataLen = headerSize + req.dataLen; + + // get the data from the base FileIO layer + ssize_t readSize = base->read( tmp ); + + if(readSize > headerSize) + { + // At this point the data has been decoded. So, compute the MAC of the + // block and check against the checksum stored in the header.. + uint64_t mac = cipher->MAC_64( tmp.data + macBytes, + readSize - macBytes, key ); + + for(int i=0; i>= 8) + { + int test = mac & 0xff; + int stored = tmp.data[i]; + if(test != stored) + { + // uh oh.. + long blockNum = req.offset / bs; + rWarning(_("MAC comparison failure in block %li"), + blockNum); + if( !warnOnly ) + { + MemoryPool::release( mb ); + throw ERROR(_("MAC comparison failure, refusing to read")); + } + break; + } + } + + // now copy the data to the output buffer + readSize -= headerSize; + memcpy( req.data, tmp.data + headerSize, readSize ); + } else + { + rDebug("readSize %i at offset %" PRIi64, (int)readSize, req.offset); + if(readSize > 0) + readSize = 0; + } + + MemoryPool::release( mb ); + + return readSize; +} + +bool MACFileIO::writeOneBlock( const IORequest &req ) +{ + int headerSize = macBytes + randBytes; + + int bs = blockSize() + headerSize; + + // we have the unencrypted data, so we need to attach a header to it. + MemBlock mb = MemoryPool::allocate( bs ); + + IORequest newReq; + newReq.offset = locWithHeader( req.offset, bs, headerSize ); + newReq.data = mb.data; + newReq.dataLen = headerSize + req.dataLen; + + memset( newReq.data, 0, headerSize ); + memcpy( newReq.data + headerSize, req.data, req.dataLen ); + if(randBytes) + cipher->randomize( newReq.data+macBytes, randBytes ); + + // compute the mac (which includes the random data) and fill it in + uint64_t mac = cipher->MAC_64( newReq.data+macBytes, + req.dataLen + randBytes, key ); + + for(int i=0; i>= 8; + } + + // now, we can let the next level have it.. + bool ok = base->write( newReq ); + + MemoryPool::release( mb ); + + return ok; +} + +int MACFileIO::truncate( off_t size ) +{ + int headerSize = macBytes + randBytes; + int bs = blockSize() + headerSize; + + int res = BlockFileIO::truncate( size, 0 ); + + if(res == 0) + base->truncate( locWithHeader( size, bs, headerSize ) ); + + return res; +} + +bool MACFileIO::isWritable() const +{ + return base->isWritable(); +} diff --git a/encfs/MACFileIO.h b/encfs/MACFileIO.h new file mode 100644 index 0000000..978b307 --- /dev/null +++ b/encfs/MACFileIO.h @@ -0,0 +1,69 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + */ + +#ifndef _MACFileIO_incl_ +#define _MACFileIO_incl_ + +#include "BlockFileIO.h" +#include "Cipher.h" + +using boost::shared_ptr; + +class MACFileIO : public BlockFileIO +{ +public: + /* + If warnOnlyMode is enabled, then a MAC comparison failure will only + result in a warning message from encfs -- the garbled data will still + be made available.. + */ + MACFileIO( const shared_ptr &base, + const shared_ptr &cipher, + const CipherKey &key, int blockSize, + int macBytes, int randBytes, + bool warnOnlyMode ); + MACFileIO(); + virtual ~MACFileIO(); + + virtual rel::Interface interface() const; + + virtual void setFileName( const char *fileName ); + virtual const char *getFileName() const; + virtual bool setIV( uint64_t iv ); + + virtual int open( int flags ); + virtual int getAttr( struct stat *stbuf ) const; + virtual off_t getSize() const; + + virtual int truncate( off_t size ); + + virtual bool isWritable() const; + +private: + virtual ssize_t readOneBlock( const IORequest &req ) const; + virtual bool writeOneBlock( const IORequest &req ); + + shared_ptr base; + shared_ptr cipher; + CipherKey key; + int macBytes; + int randBytes; + bool warnOnly; +}; + +#endif + diff --git a/encfs/Makefile.am b/encfs/Makefile.am new file mode 100644 index 0000000..6215107 --- /dev/null +++ b/encfs/Makefile.am @@ -0,0 +1,138 @@ + +include $(top_srcdir)/Makefile.common + +ALL_INCLUDES = @RLOG_CFLAGS@ @OPENSSL_CFLAGS@ +ALL_LDFLAGS = @RLOG_LIBS@ @OPENSSL_LIBS@ @FUSE_LIBS@ + +INCLUDES = $(all_includes) -I../intl + +AM_CXXFLAGS = -DRLOG_COMPONENT="encfs" $(ALL_INCLUDES) + +if BUILD_NLS +# define a C macro LOCALEDIR indicating where catalogs will be installed +#localedir = $(datadir)/locale + +AM_CXXFLAGS += -DLOCALEDIR=\"$(localedir)\" +ALL_LDFLAGS += @LIBINTL@ +endif + +lib_LTLIBRARIES = libencfs.la +bin_PROGRAMS = encfs encfsctl +dist_bin_SCRIPTS = encfssh +noinst_PROGRAMS = test + +all-local: encfs-man.html + +#encfs_LDADD = libencfs.la -lfuse +#encfsctl_LDADD = libencfs.la -lfuse +#test_LDADD = libencfs.la -lfuse +encfs_LDADD = libencfs.la $(ALL_LDFLAGS) +encfsctl_LDADD = libencfs.la $(ALL_LDFLAGS) +test_LDADD = libencfs.la $(ALL_LDFLAGS) + +if BUILD_STATIC +encfs_LDFLAGS = -all-static +encfsctl_LDFLAGS = -all-static +test_LDFLAGS = -all-static +endif + +# CURRENT : REVISION : AGE +# +1 : 0 : +1 => new interface that does not break old one +# +1 : 0 : 0 => new interface that breaks old one +# : : 0 => no new interfaces, but breaks old apps +# : +1 : => internal changes, nothing breaks +# +libencfs_la_LDFLAGS = -version-info 3:0:0 +libencfs_la_LIBADD = -lrlog + +EXTRASRC = ../intl/autosprintf.cpp +if BUILD_OPENSSL +if BUILD_SSLCIPHER +EXTRASRC += SSL_Cipher.cpp +endif +endif + +libencfs_la_SOURCES = \ + readpassphrase.cpp \ + base64.cpp \ + ConfigReader.cpp \ + ConfigVar.cpp \ + Context.cpp \ + Cipher.cpp \ + CipherKey.cpp \ + FileIO.cpp \ + RawFileIO.cpp \ + BlockFileIO.cpp \ + CipherFileIO.cpp \ + MACFileIO.cpp \ + NameIO.cpp \ + StreamNameIO.cpp \ + BlockNameIO.cpp \ + NullNameIO.cpp \ + Interface.cpp \ + MemoryPool.cpp \ + NullCipher.cpp \ + DirNode.cpp \ + FileNode.cpp \ + FileUtils.cpp \ + ${EXTRASRC} + + +encfs_SOURCES = \ + encfs.cpp \ + openssl.cpp \ + main.cpp + +test_SOURCES = \ + test.cpp + +encfsctl_SOURCES = \ + encfsctl.cpp + +noinst_HEADERS = \ + base64.h \ + BlockFileIO.h \ + BlockNameIO.h \ + CipherFileIO.h \ + Cipher.h \ + CipherKey.h \ + ConfigReader.h \ + ConfigVar.h \ + Context.h \ + DirNode.h \ + encfs.h \ + FileIO.h \ + FileNode.h \ + FileUtils.h \ + Interface.h \ + i18n.h \ + MACFileIO.h \ + MemoryPool.h \ + Mutex.h \ + NameIO.h \ + NullCipher.h \ + NullNameIO.h \ + openssl.h \ + Range.h \ + RawFileIO.h \ + readpassphrase.h \ + SSL_Cipher.h \ + StreamNameIO.h + +man_MANS=encfs.1 encfsctl.1 +EXTRA_DIST = encfs.pod encfsctl.pod encfs.1 encfsctl.1 encfs-man.html + +if BUILD_MAN +SUFFIXES = .1 .pod +# since we have POD2MAN, we can specify how to rebuild encfs.1 if necessary +.pod.1: + @POD2MAN@ --section=1 --release=@VERSION@ --center="Encrypted Filesystem" $< $@ + +CLEANFILES = encfs.1 encfsctl.1 +endif + +if BUILD_MANHTML +encfs-man.html: encfs.pod + @POD2HTML@ encfs.pod > $@ +endif + diff --git a/encfs/MemoryPool.cpp b/encfs/MemoryPool.cpp new file mode 100644 index 0000000..0421f35 --- /dev/null +++ b/encfs/MemoryPool.cpp @@ -0,0 +1,151 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2003, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + * + */ + +#include "MemoryPool.h" +#include + +#include +#include + +#include "config.h" +#include + +#ifdef HAVE_VALGRIND_MEMCHECK_H +#include +#else +#define VALGRIND_MAKE_MEM_NOACCESS( a, b ) +#define VALGRIND_MAKE_MEM_UNDEFINED( a, b ) +#endif + +using namespace rlog; + +#if HAVE_SSL + +# include +#define BLOCKDATA( BLOCK ) (unsigned char*)BLOCK->data->data + +#endif + + +struct BlockList +{ + BlockList *next; + int size; +#ifdef HAVE_SSL + BUF_MEM *data; +#endif +}; + +static BlockList *allocBlock( int size ) +{ + BlockList *block = new BlockList; + block->size = size; +#ifdef HAVE_SSL + block->data = BUF_MEM_new( ); + BUF_MEM_grow( block->data, size ); + VALGRIND_MAKE_MEM_NOACCESS( block->data->data, block->data->max ); +#endif + return block; +} + +static void freeBlock( BlockList *el ) +{ +#ifdef HAVE_SSL + VALGRIND_MAKE_MEM_UNDEFINED( el->data->data, el->data->max ); + BUF_MEM_free( el->data ); +#endif + delete el; +} + +static pthread_mutex_t gMPoolMutex = PTHREAD_MUTEX_INITIALIZER; +static BlockList *gMemPool = NULL; + + + +MemBlock MemoryPool::allocate( int size ) +{ + pthread_mutex_lock( &gMPoolMutex ); + + BlockList *parent = NULL; + BlockList *block = gMemPool; + // check if we already have a large enough block available.. + while(block != NULL && block->size < size) + { + parent = block; + block = block->next; + } + + // unlink block from list + if(block) + { + if(!parent) + gMemPool = block->next; + else + parent->next = block->next; + } + pthread_mutex_unlock( &gMPoolMutex ); + + if(!block) + block = allocBlock( size ); + block->next = NULL; + + MemBlock result; + result.data = BLOCKDATA(block); + result.internalData = block; + + VALGRIND_MAKE_MEM_UNDEFINED( result.data, size ); + + return result; +} + +void MemoryPool::release( const MemBlock &mb ) +{ + pthread_mutex_lock( &gMPoolMutex ); + + BlockList *block = (BlockList*)mb.internalData; + + // just to be sure there's nothing important left in buffers.. + VALGRIND_MAKE_MEM_UNDEFINED( block->data->data, block->size ); + memset( BLOCKDATA(block) , 0, block->size); + VALGRIND_MAKE_MEM_NOACCESS( block->data->data, block->data->max ); + + block->next = gMemPool; + gMemPool = block; + + pthread_mutex_unlock( &gMPoolMutex ); +} + +void MemoryPool::destroyAll() +{ + pthread_mutex_lock( &gMPoolMutex ); + + BlockList *block = gMemPool; + gMemPool = NULL; + + pthread_mutex_unlock( &gMPoolMutex ); + + while(block != NULL) + { + BlockList *next = block->next; + + freeBlock( block ); + block = next; + } +} + + diff --git a/encfs/MemoryPool.h b/encfs/MemoryPool.h new file mode 100644 index 0000000..b1edcda --- /dev/null +++ b/encfs/MemoryPool.h @@ -0,0 +1,54 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2003, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (LGPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + * + */ + +#ifndef _MemoryPool_incl_ +#define _MemoryPool_incl_ + + +struct MemBlock +{ + unsigned char *data; + + void *internalData; + + MemBlock(); +}; + +inline MemBlock::MemBlock() + : data(0), internalData(0) +{ +} + +/* + Memory Pool for fixed sized objects. + + Usage: + MemBlock mb = MemoryPool::allocate( size ); + // do things with storage in mb.data + unsigned char *buffer = mb.data; + MemoryPool::release( mb ); +*/ +namespace MemoryPool +{ + MemBlock allocate( int size ); + void release( const MemBlock &el ); + void destroyAll(); +} + +#endif + diff --git a/encfs/Mutex.h b/encfs/Mutex.h new file mode 100644 index 0000000..b443e76 --- /dev/null +++ b/encfs/Mutex.h @@ -0,0 +1,62 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2003, Valient Gough + * + * This program is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef _Mutex_incl_ +#define _Mutex_incl_ + +#include + +namespace rel +{ + + class Lock + { + public: + Lock( pthread_mutex_t &mutex ); + ~Lock(); + + // leave the lock as it is. When the Lock wrapper is destroyed, it + // will do nothing with the pthread mutex. + void leave(); + + private: + Lock(const Lock &src); // not allowed + Lock &operator = (const Lock &src); // not allowed + + pthread_mutex_t *_mutex; + }; + + inline Lock::Lock( pthread_mutex_t &mutex ) + : _mutex( &mutex ) + { + pthread_mutex_lock( _mutex ); + } + + inline Lock::~Lock( ) + { + if(_mutex) + pthread_mutex_unlock( _mutex ); + } + + inline void Lock::leave() + { + _mutex = 0; + } +} + +#endif + diff --git a/encfs/NameIO.cpp b/encfs/NameIO.cpp new file mode 100644 index 0000000..8c28f16 --- /dev/null +++ b/encfs/NameIO.cpp @@ -0,0 +1,348 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + */ + +#include "NameIO.h" +#include "config.h" + +#include +#include + +#include + +// for static build. Need to reference the modules which are registered at +// run-time, to ensure that the linker doesn't optimize them away. +#include +#include "BlockNameIO.h" +#include "StreamNameIO.h" +#include "NullNameIO.h" + +using namespace std; +using namespace rel; +using namespace rlog; + +#define REF_MODULE(TYPE) \ + if(!TYPE::Enabled() ) \ + cerr << "referenceModule: should never happen\n"; + +static +void AddSymbolReferences() +{ + REF_MODULE(BlockNameIO) + REF_MODULE(StreamNameIO) + REF_MODULE(NullNameIO) +} + + +struct NameIOAlg +{ + bool hidden; + NameIO::Constructor constructor; + string description; + Interface iface; +}; + +typedef multimap< string, NameIOAlg > NameIOMap_t; +static NameIOMap_t *gNameIOMap = 0; + + +list< NameIO::Algorithm > +NameIO::GetAlgorithmList( bool includeHidden ) +{ + AddSymbolReferences(); + + list< Algorithm > result; + if(gNameIOMap) + { + NameIOMap_t::const_iterator it; + NameIOMap_t::const_iterator end = gNameIOMap->end(); + for(it = gNameIOMap->begin(); it != end; ++it) + { + if(includeHidden || !it->second.hidden) + { + Algorithm tmp; + tmp.name = it->first; + tmp.description = it->second.description; + tmp.iface = it->second.iface; + + result.push_back( tmp ); + } + } + } + + return result; +} + +bool NameIO::Register( const char *name, const char *description, + const Interface &iface, Constructor constructor, + bool hidden ) +{ + if( !gNameIOMap ) + gNameIOMap = new NameIOMap_t; + + NameIOAlg alg; + alg.hidden = hidden; + alg.constructor = constructor; + alg.description = description; + alg.iface = iface; + + gNameIOMap->insert( make_pair( string(name), alg )); + return true; +} + +shared_ptr NameIO::New( const string &name, + const shared_ptr &cipher, const CipherKey &key) +{ + shared_ptr result; + if(gNameIOMap) + { + NameIOMap_t::const_iterator it = gNameIOMap->find( name ); + if(it != gNameIOMap->end()) + { + Constructor fn = it->second.constructor; + result = (*fn)( it->second.iface, cipher, key ); + } + } + return result; +} + +shared_ptr NameIO::New( const Interface &iface, + const shared_ptr &cipher, const CipherKey &key ) +{ + shared_ptr result; + if(gNameIOMap) + { + NameIOMap_t::const_iterator it; + NameIOMap_t::const_iterator end = gNameIOMap->end(); + for(it = gNameIOMap->begin(); it != end; ++it) + { + if( it->second.iface.implements( iface )) + { + Constructor fn = it->second.constructor; + result = (*fn)( iface, cipher, key ); + break; + } + } + } + return result; +} + + + +NameIO::NameIO() + : chainedNameIV( false ), reverseEncryption( false ) +{ +} + +NameIO::~NameIO() +{ +} + +void NameIO::setChainedNameIV( bool enable ) +{ + chainedNameIV = enable; +} + +bool NameIO::getChainedNameIV() const +{ + return chainedNameIV; +} + +void NameIO::setReverseEncryption( bool enable ) +{ + reverseEncryption = enable; +} + +bool NameIO::getReverseEncryption() const +{ + return reverseEncryption; +} + + +std::string NameIO::recodePath( const char *path, + int (NameIO::*_length)(int) const, + int (NameIO::*_code)(const char*, int, uint64_t *, char*) const, + uint64_t *iv ) const +{ + string output; + + while( *path ) + { + if( *path == '/' ) + { + if( !output.empty() ) // don't start the string with '/' + output += '/'; + ++path; + } else + { + bool isDotFile = (*path == '.'); + char *next = strchr( path, '/' ); + int len = next ? next - path : strlen( path ); + + // at this point we know that len > 0 + if( isDotFile && (path[len-1] == '.') && (len <= 2) ) + { + output.append(len, '.'); // append [len] copies of '.' + path += len; + continue; + } + + // figure out buffer sizes + int approxLen = (this->*_length)( len ); + if(approxLen <= 0) + throw ERROR("Filename too small to decode"); + + BUFFER_INIT( codeBuf, 32, (unsigned int)approxLen+1 ) + + // code the name + int codedLen = (this->*_code)( path, len, iv, codeBuf ); + rAssert( codedLen <= approxLen ); + rAssert( codeBuf[codedLen] == '\0' ); + path += len; + + // append result to string + output += (char*)codeBuf; + + BUFFER_RESET( codeBuf ) + } + } + + return output; +} + +std::string NameIO::encodePath( const char *plaintextPath ) const +{ + uint64_t iv = 0; + return encodePath( plaintextPath, &iv); +} + +std::string NameIO::decodePath( const char *cipherPath ) const +{ + uint64_t iv = 0; + return decodePath( cipherPath, &iv ); +} + +std::string NameIO::_encodePath( const char *plaintextPath, uint64_t *iv ) const +{ + // if chaining is not enabled, then the iv pointer is not used.. + if(!chainedNameIV) + iv = 0; + return recodePath( plaintextPath, + &NameIO::maxEncodedNameLen, &NameIO::encodeName, iv); +} + +std::string NameIO::_decodePath( const char *cipherPath, uint64_t *iv ) const +{ + // if chaining is not enabled, then the iv pointer is not used.. + if(!chainedNameIV) + iv = 0; + return recodePath( cipherPath, + &NameIO::maxDecodedNameLen, &NameIO::decodeName, iv); +} + +std::string NameIO::encodePath( const char *path, uint64_t *iv ) const +{ + return getReverseEncryption() ? + _decodePath( path, iv ) : + _encodePath( path, iv ); +} + +std::string NameIO::decodePath( const char *path, uint64_t *iv ) const +{ + return getReverseEncryption() ? + _encodePath( path, iv ) : + _decodePath( path, iv ); +} + + +int NameIO::encodeName( const char *input, int length, char *output ) const +{ + return encodeName( input, length, (uint64_t*)0, output ); +} + +int NameIO::decodeName( const char *input, int length, char *output ) const +{ + return decodeName( input, length, (uint64_t*)0, output ); +} + +std::string NameIO::_encodeName( const char *plaintextName, int length ) const +{ + int approxLen = maxEncodedNameLen( length ); + + BUFFER_INIT( codeBuf, 32, (unsigned int)approxLen+1 ) + + // code the name + int codedLen = encodeName( plaintextName, length, 0, codeBuf ); + rAssert( codedLen <= approxLen ); + rAssert( codeBuf[codedLen] == '\0' ); + + // append result to string + std::string result = (char*)codeBuf; + + BUFFER_RESET( codeBuf ) + + return result; +} + +std::string NameIO::_decodeName( const char *encodedName, int length ) const +{ + int approxLen = maxDecodedNameLen( length ); + + BUFFER_INIT( codeBuf, 32, (unsigned int)approxLen+1 ) + + // code the name + int codedLen = decodeName( encodedName, length, 0, codeBuf ); + rAssert( codedLen <= approxLen ); + rAssert( codeBuf[codedLen] == '\0' ); + + // append result to string + std::string result = (char*)codeBuf; + + BUFFER_RESET( codeBuf ) + + return result; +} + +std::string NameIO::encodeName( const char *path, int length ) const +{ + return getReverseEncryption() ? + _decodeName( path, length ) : + _encodeName( path, length ); +} + +std::string NameIO::decodeName( const char *path, int length ) const +{ + return getReverseEncryption() ? + _encodeName( path, length ) : + _decodeName( path, length ); +} +/* +int NameIO::encodeName( const char *path, int length, + char *output ) const +{ + return getReverseEncryption() ? + _decodeName( path, length, output ) : + _encodeName( path, length, output ); +} + +int NameIO::decodeName( const char *path, int length, + char *output ) const +{ + return getReverseEncryption() ? + _encodeName( path, length, output ) : + _decodeName( path, length, output ); +} +*/ diff --git a/encfs/NameIO.h b/encfs/NameIO.h new file mode 100644 index 0000000..0017bd7 --- /dev/null +++ b/encfs/NameIO.h @@ -0,0 +1,136 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + */ + +#ifndef _NameIO_incl_ +#define _NameIO_incl_ + +#include +#include + +#include + +#include "Interface.h" +#include "CipherKey.h" + +using boost::shared_ptr; +class Cipher; + +class NameIO +{ +public: + typedef shared_ptr (*Constructor)( const rel::Interface &iface, + const shared_ptr &cipher, const CipherKey &key); + + struct Algorithm + { + std::string name; + std::string description; + rel::Interface iface; + }; + + typedef std::list AlgorithmList; + static AlgorithmList GetAlgorithmList( bool includeHidden = false ); + + static shared_ptr New( const rel::Interface &iface, + const shared_ptr &cipher, const CipherKey &key); + static shared_ptr New( const std::string &name, + const shared_ptr &cipher, const CipherKey &key); + + static bool Register( const char *name, const char *description, + const rel::Interface &iface, Constructor constructor, + bool hidden = false); + + + NameIO(); + virtual ~NameIO(); + + virtual rel::Interface interface() const =0; + + void setChainedNameIV( bool enable ); + bool getChainedNameIV() const; + void setReverseEncryption( bool enable ); + bool getReverseEncryption() const; + + std::string encodePath( const char *plaintextPath ) const; + std::string decodePath( const char *encodedPath ) const; + + std::string encodePath( const char *plaintextPath, uint64_t *iv ) const; + std::string decodePath( const char *encodedPath, uint64_t *iv ) const; + + virtual int maxEncodedNameLen( int plaintextNameLen ) const =0; + virtual int maxDecodedNameLen( int encodedNameLen ) const =0; + + std::string encodeName( const char *plaintextName, int length ) const; + std::string decodeName( const char *encodedName, int length ) const; + +protected: + virtual int encodeName( const char *plaintextName, int length, + char *encodedName ) const; + virtual int decodeName( const char *encodedName, int length, + char *plaintextName ) const; + + virtual int encodeName( const char *plaintextName, int length, + uint64_t *iv, char *encodedName ) const =0; + virtual int decodeName( const char *encodedName, int length, + uint64_t *iv, char *plaintextName ) const =0; + + +private: + + std::string recodePath( const char *path, + int (NameIO::*codingLen)(int) const, + int (NameIO::*codingFunc)(const char *, int, + uint64_t *, char *) const, + uint64_t *iv ) const; + + std::string _encodePath( const char *plaintextPath, uint64_t *iv ) const; + std::string _decodePath( const char *encodedPath, uint64_t *iv ) const; + std::string _encodeName( const char *plaintextName, int length ) const; + std::string _decodeName( const char *encodedName, int length ) const; + + bool chainedNameIV; + bool reverseEncryption; +}; + + + +/* + Helper macros for creating temporary buffers with an optimization that + below a given size (OptimizedSize) is allocated on the stack, and when a + larger size is requested it is allocated on the heap. + + BUFFER_RESET should be called for the same name as BUFFER_INIT +*/ +#define BUFFER_INIT( Name, OptimizedSize, Size ) \ +char Name ## _Raw [ OptimizedSize ]; \ +char *Name = Name ## _Raw; \ +if( sizeof(Name ## _Raw) < Size ) \ + Name = new char[ Size ];\ +memset( Name, 0, Size ); + +#define BUFFER_RESET( Name ) \ +do { \ + if( Name != Name ## _Raw ) \ + { \ + delete[] Name; \ + Name = Name ## _Raw; \ + } \ +} while(0); + + +#endif + diff --git a/encfs/NullCipher.cpp b/encfs/NullCipher.cpp new file mode 100644 index 0000000..fa64b8e --- /dev/null +++ b/encfs/NullCipher.cpp @@ -0,0 +1,177 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + */ + +#include "NullCipher.h" + +#include "Range.h" +#include "Interface.h" + +#include +#include + +#include + +using namespace std; +using namespace rel; +using namespace rlog; +using boost::shared_ptr; +using boost::dynamic_pointer_cast; + + +static Interface NullInterface( "nullCipher", 1, 0, 0 ); +static Range NullKeyRange(0); +static Range NullBlockRange(1,4096,1); + +static shared_ptr NewNullCipher(const Interface &iface, int keyLen) +{ + (void)keyLen; + return shared_ptr( new NullCipher( iface ) ); +} + +const bool HiddenCipher = true; + +static bool NullCipher_registered = Cipher::Register("Null", + "Non encrypting cipher. For testing only!", + NullInterface, NullKeyRange, NullBlockRange, NewNullCipher, + HiddenCipher); + +class NullKey : public AbstractCipherKey +{ +public: + NullKey() {} + virtual ~NullKey() {} +}; + +class NullDestructor +{ +public: + NullDestructor() {} + NullDestructor(const NullDestructor &) {} + ~NullDestructor() {} + + NullDestructor &operator = (const NullDestructor &){ return *this; } + void operator ()(NullKey *&) {} +}; + +shared_ptr gNullKey( new NullKey(), NullDestructor() ); + +NullCipher::NullCipher(const Interface &iface_) +{ + this->iface = iface_; +} + +NullCipher::~NullCipher() +{ +} + +Interface NullCipher::interface() const +{ + return iface; +} + +CipherKey NullCipher::newKey(const char *, int ) +{ + return gNullKey; +} + +CipherKey NullCipher::newRandomKey() +{ + return gNullKey; +} + +void NullCipher::randomize( unsigned char *buf, int len ) const +{ + memset( buf, 0, len ); +} + +uint64_t NullCipher::MAC_64(const unsigned char *, int , + const CipherKey &, uint64_t *) const +{ + return 0; +} + +CipherKey NullCipher::readKey( const unsigned char *, + const CipherKey &, bool) +{ + return gNullKey; +} + +void NullCipher::writeKey(const CipherKey &, unsigned char *, + const CipherKey &) +{ +} + +bool NullCipher::compareKey(const CipherKey &A_, + const CipherKey &B_) const +{ + shared_ptr A = dynamic_pointer_cast(A_); + shared_ptr B = dynamic_pointer_cast(B_); + return A.get() == B.get(); +} + +int NullCipher::encodedKeySize() const +{ + return 0; +} + +int NullCipher::keySize() const +{ + return 0; +} + +int NullCipher::cipherBlockSize() const +{ + return 1; +} + +bool NullCipher::streamEncode( unsigned char *src, int len, + uint64_t iv64, const CipherKey &key) const +{ + (void)src; + (void)len; + (void)iv64; + (void)key; + return true; +} + +bool NullCipher::streamDecode( unsigned char *src, int len, + uint64_t iv64, const CipherKey &key) const +{ + (void)src; + (void)len; + (void)iv64; + (void)key; + return true; +} + +bool NullCipher::blockEncode( unsigned char *, int , uint64_t, + const CipherKey & ) const +{ + return true; +} + +bool NullCipher::blockDecode( unsigned char *, int, uint64_t, + const CipherKey & ) const +{ + return true; +} + +bool NullCipher::Enabled() +{ + return true; +} + diff --git a/encfs/NullCipher.h b/encfs/NullCipher.h new file mode 100644 index 0000000..3236103 --- /dev/null +++ b/encfs/NullCipher.h @@ -0,0 +1,80 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + * + */ + +#ifndef _NullCipher_incl_ +#define _NullCipher_incl_ + +#include "Cipher.h" +#include "Interface.h" + +/* + Implements Cipher interface for a pass-through mode. May be useful for + testing, but that's it. +*/ +class NullCipher : public Cipher +{ + rel::Interface iface; + +public: + NullCipher(const rel::Interface &iface); + virtual ~NullCipher(); + + virtual rel::Interface interface() const; + + // create a new key based on a password + virtual CipherKey newKey(const char *password, int passwdLength); + // create a new random key + virtual CipherKey newRandomKey(); + + // data must be len keySize() + virtual CipherKey readKey(const unsigned char *data, + const CipherKey &encodingKey, + bool checkKey); + virtual void writeKey(const CipherKey &key, unsigned char *data, + const CipherKey &encodingKey); + virtual bool compareKey( const CipherKey &A, + const CipherKey &B ) const; + + // meta-data about the cypher + virtual int keySize() const; + virtual int encodedKeySize() const; + virtual int cipherBlockSize() const; + + virtual void randomize( unsigned char *buf, int len ) const; + + virtual uint64_t MAC_64(const unsigned char *data, int len, + const CipherKey &key, uint64_t *chainedIV) const; + + // functional interfaces + virtual bool streamEncode(unsigned char *in, int len, + uint64_t iv64, const CipherKey &key) const; + virtual bool streamDecode(unsigned char *in, int len, + uint64_t iv64, const CipherKey &key) const; + + virtual bool blockEncode(unsigned char *buf, int size, + uint64_t iv64, const CipherKey &key) const; + virtual bool blockDecode(unsigned char *buf, int size, + uint64_t iv64, const CipherKey &key) const; + + // hack to help with static builds + static bool Enabled(); +}; + + +#endif + diff --git a/encfs/NullNameIO.cpp b/encfs/NullNameIO.cpp new file mode 100644 index 0000000..0e76da0 --- /dev/null +++ b/encfs/NullNameIO.cpp @@ -0,0 +1,86 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + */ + +#include "NullNameIO.h" + +#include "Cipher.h" +#include "base64.h" + +using namespace rel; +using boost::shared_ptr; + +static shared_ptr NewNNIO( const Interface &, + const shared_ptr &, const CipherKey & ) +{ + return shared_ptr( new NullNameIO() ); +} + +static Interface NNIOIface("nameio/null", 1, 0, 0); +static bool NullNameIO_registered = NameIO::Register("Null", + "No encryption of filenames", NNIOIface, NewNNIO); + +NullNameIO::NullNameIO( ) +{ + +} + +NullNameIO::~NullNameIO() +{ +} + +Interface NullNameIO::interface() const +{ + return NNIOIface; +} + +Interface NullNameIO::CurrentInterface() +{ + return NNIOIface; +} + + +int NullNameIO::maxEncodedNameLen( int plaintextNameLen ) const +{ + return plaintextNameLen; +} + +int NullNameIO::maxDecodedNameLen( int encodedNameLen ) const +{ + return encodedNameLen; +} + +int NullNameIO::encodeName( const char *plaintextName, int length, + uint64_t *iv, char *encodedName ) const +{ + memcpy( encodedName, plaintextName, length ); + + return length; +} + +int NullNameIO::decodeName( const char *encodedName, int length, + uint64_t *iv, char *plaintextName ) const +{ + memcpy( plaintextName, encodedName, length ); + + return length; +} + +bool NullNameIO::Enabled() +{ + return true; +} + diff --git a/encfs/NullNameIO.h b/encfs/NullNameIO.h new file mode 100644 index 0000000..e86368b --- /dev/null +++ b/encfs/NullNameIO.h @@ -0,0 +1,49 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + */ + +#ifndef _NullNameIO_incl_ +#define _NullNameIO_incl_ + +#include "NameIO.h" + +class NullNameIO : public NameIO +{ +public: + static rel::Interface CurrentInterface(); + + NullNameIO( ); + + virtual ~NullNameIO(); + + virtual rel::Interface interface() const; + + virtual int maxEncodedNameLen( int plaintextNameLen ) const; + virtual int maxDecodedNameLen( int encodedNameLen ) const; + + // hack to help with static builds + static bool Enabled(); +protected: + virtual int encodeName( const char *plaintextName, int length, + uint64_t *iv, char *encodedName ) const; + virtual int decodeName( const char *encodedName, int length, + uint64_t *iv, char *plaintextName ) const; +private: +}; + + +#endif + diff --git a/encfs/Range.h b/encfs/Range.h new file mode 100644 index 0000000..8c60e0c --- /dev/null +++ b/encfs/Range.h @@ -0,0 +1,110 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This program is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef _Range_incl_ +#define _Range_incl_ + + +class Range +{ + int minVal; + int maxVal; + int increment; +public: + Range(); + Range(int minMax); + Range(int min, int max, int increment); + + bool allowed(int value) const; + + int closest(int value) const; + + int min() const; + int max() const; + int inc() const; +}; + +inline Range::Range(int minMax) +{ + this->minVal = minMax; + this->maxVal = minMax; + this->increment = 1; +} + +inline Range::Range(int min_, int max_, int increment_) +{ + this->minVal = min_; + this->maxVal = max_; + this->increment = increment_; + if(increment == 0) + this->increment = 1; +} + +inline Range::Range() + : minVal(-1) + , maxVal(-1) + , increment(1) +{ +} + +inline bool Range::allowed(int value) const +{ + if(value >= minVal && value <= maxVal) + { + int tmp = value - minVal; + if((tmp % increment) == 0) + return true; + } + return false; +} + +inline int Range::closest(int value) const +{ + if(allowed(value)) + return value; + else + if(value < minVal) + return minVal; + else + if(value > maxVal) + return maxVal; + + // must be inbetween but not matched with increment + int tmp = value - minVal; + // try rounding to the nearest increment.. + tmp += (increment >> 1); + tmp -= (tmp % increment); + + return closest( value + tmp ); +} + +inline int Range::min() const +{ + return minVal; +} + +inline int Range::max() const +{ + return maxVal; +} + +inline int Range::inc() const +{ + return increment; +} + +#endif diff --git a/encfs/RawFileIO.cpp b/encfs/RawFileIO.cpp new file mode 100644 index 0000000..ac28664 --- /dev/null +++ b/encfs/RawFileIO.cpp @@ -0,0 +1,322 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + */ + +#ifdef linux +#define _XOPEN_SOURCE 500 // pick up pread , pwrite +#endif +#include + +#include "RawFileIO.h" + +#include + +#include +#include +#include +#include + +#include + +using namespace std; + +static rel::Interface RawFileIO_iface("FileIO/Raw", 1, 0, 0); + +FileIO *NewRawFileIO( const rel::Interface &iface ) +{ + (void)iface; + return new RawFileIO(); +} + +inline void swap( int &x, int &y ) +{ + int tmp = x; + x = y; + y = tmp; +} + +RawFileIO::RawFileIO( ) + : knownSize( false ) + , fileSize(0) + , fd( -1 ) + , oldfd( -1 ) + , canWrite( false ) +{ +} + +RawFileIO::RawFileIO( const std::string &fileName ) + : name( fileName ) + , knownSize( false ) + , fileSize( 0 ) + , fd( -1 ) + , oldfd( -1 ) + , canWrite( false ) +{ +} + +RawFileIO::~RawFileIO() +{ + int _fd = -1; + int _oldfd = -1; + + swap( _fd, fd ); + swap( _oldfd, oldfd ); + + if( _oldfd != -1 ) + close( _oldfd ); + + if( _fd != -1 ) + close( _fd ); +} + +rel::Interface RawFileIO::interface() const +{ + return RawFileIO_iface; +} + +/* + Workaround for opening a file for write when permissions don't allow. + Since the kernel has already checked permissions, we can assume it is ok to + provide access. So force it by changing permissions temporarily. Should + be called with a lock around it so that there won't be a race condition + with calls to lstat picking up the wrong permissions. +*/ +static int open_readonly_workaround(const char *path, int flags) +{ + int fd = -1; + struct stat stbuf; + memset(&stbuf, 0, sizeof(struct stat)); + if(lstat( path, &stbuf ) != -1) + { + // make sure user has read/write permission.. + chmod( path , stbuf.st_mode | 0600 ); + fd = ::open( path , flags ); + chmod( path , stbuf.st_mode ); + } else + { + rInfo("can't stat file %s", path ); + } + + return fd; +} + +/* + We shouldn't have to support all possible open flags, so untaint the flags + argument by only taking ones we understand and accept. + - Since the kernel has already done permission tests before calling us, we + shouldn't have to worry about access control. + - Basically we just need to distinguish between read and write flags + - Also keep the O_LARGEFILE flag, in case the underlying filesystem needs + it.. +*/ +int RawFileIO::open(int flags) +{ + bool requestWrite = ((flags & O_RDWR) || (flags & O_WRONLY)); + + rDebug("open call for %s file", requestWrite ? "writable" : "read only"); + + int result = 0; + + // if we have a descriptor and it is writable, or we don't need writable.. + if((fd >= 0) && (canWrite || !requestWrite)) + { + rDebug("using existing file descriptor"); + result = fd; // success + } else + { + int finalFlags = requestWrite ? O_RDWR : O_RDONLY; + +#if defined(O_LARGEFILE) + if( flags & O_LARGEFILE ) + finalFlags |= O_LARGEFILE; +#else +#warning O_LARGEFILE not supported +#endif + + int newFd = ::open( name.c_str(), finalFlags ); + + rDebug("open file with flags %i, result = %i", finalFlags, newFd); + + if((newFd == -1) && (errno == EACCES)) + { + rDebug("using readonly workaround for open"); + newFd = open_readonly_workaround( name.c_str(), finalFlags ); + } + + if(newFd >= 0) + { + if(oldfd >= 0) + { + rError("leaking FD?: oldfd = %i, fd = %i, newfd = %i", + oldfd, fd, newFd); + } + + // the old fd might still be in use, so just keep it around for + // now. + canWrite = requestWrite; + oldfd = fd; + result = fd = newFd; + } else + { + result = -errno; + rInfo("::open error: %s", strerror(errno)); + } + } + + if(result < 0) + rInfo("file %s open failure: %i", name.c_str(), -result); + + return result; +} + +int RawFileIO::getAttr( struct stat *stbuf ) const +{ + int res = lstat( name.c_str(), stbuf ); + int eno = errno; + + if(res < 0) + rInfo("getAttr error on %s: %s", name.c_str(), strerror( eno )); + + return ( res < 0 ) ? -eno : 0; +} + +void RawFileIO::setFileName( const char *fileName ) +{ + name = fileName; +} + +const char *RawFileIO::getFileName() const +{ + return name.c_str(); +} + +off_t RawFileIO::getSize() const +{ + if(!knownSize) + { + struct stat stbuf; + memset( &stbuf, 0, sizeof( struct stat )); + int res = lstat( name.c_str(), &stbuf ); + + if(res == 0) + { + const_cast(this)->fileSize = stbuf.st_size; + const_cast(this)->knownSize = true; + return fileSize; + } else + return -1; + } else + { + return fileSize; + } +} + +ssize_t RawFileIO::read( const IORequest &req ) const +{ + rAssert( fd >= 0 ); + + ssize_t readSize = pread( fd, req.data, req.dataLen, req.offset ); + + if(readSize < 0) + { + rInfo("read failed at offset %" PRIi64 " for %i bytes: %s", + req.offset, req.dataLen, strerror( errno )); + } + + return readSize; +} + +bool RawFileIO::write( const IORequest &req ) +{ + rAssert( fd >= 0 ); + rAssert( true == canWrite ); + + int retrys = 10; + void *buf = req.data; + ssize_t bytes = req.dataLen; + off_t offset = req.offset; + + while( bytes && retrys > 0 ) + { + ssize_t writeSize = ::pwrite( fd, buf, bytes, offset ); + + if( writeSize < 0 ) + { + knownSize = false; + rInfo("write failed at offset %" PRIi64 " for %i bytes: %s", + offset, (int)bytes, strerror( errno )); + return false; + } + + bytes -= writeSize; + offset += writeSize; + buf = (void*)((char*)buf + writeSize); + --retrys; + } + + if(bytes != 0) + { + rError("Write error: wrote %i bytes of %i, max retries reached\n", + (int)(req.dataLen - bytes), req.dataLen ); + knownSize = false; + return false; + } else + { + if(knownSize) + { + off_t last = req.offset + req.dataLen; + if(last > fileSize) + fileSize = last; + } + + return true; + } +} + +int RawFileIO::truncate( off_t size ) +{ + int res; + + if(fd >= 0 && canWrite) + { + res = ::ftruncate( fd, size ); +#ifndef __FreeBSD__ + ::fdatasync( fd ); +#endif + } else + res = ::truncate( name.c_str(), size ); + + if(res < 0) + { + int eno = errno; + rInfo("truncate failed for %s (%i) size %" PRIi64 ", error %s", + name.c_str(), fd, size, strerror(eno)); + res = -eno; + knownSize = false; + } else + { + res = 0; + fileSize = size; + knownSize = true; + } + + return res; +} + +bool RawFileIO::isWritable() const +{ + return canWrite; +} diff --git a/encfs/RawFileIO.h b/encfs/RawFileIO.h new file mode 100644 index 0000000..6e481ee --- /dev/null +++ b/encfs/RawFileIO.h @@ -0,0 +1,61 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + */ + +#ifndef _RawFileIO_incl_ +#define _RawFileIO_incl_ + +#include "FileIO.h" + +#include + +class RawFileIO : public FileIO +{ +public: + RawFileIO(); + RawFileIO( const std::string &fileName ); + virtual ~RawFileIO(); + + virtual rel::Interface interface() const; + + virtual void setFileName( const char *fileName ); + virtual const char *getFileName() const; + + virtual int open( int flags ); + + virtual int getAttr( struct stat *stbuf ) const; + virtual off_t getSize() const; + + virtual ssize_t read( const IORequest & req ) const; + virtual bool write( const IORequest &req ); + + virtual int truncate( off_t size ); + + virtual bool isWritable() const; +protected: + + std::string name; + + bool knownSize; + off_t fileSize; + + int fd; + int oldfd; + bool canWrite; +}; + +#endif + diff --git a/encfs/SSL_Cipher.cpp b/encfs/SSL_Cipher.cpp new file mode 100644 index 0000000..f65e468 --- /dev/null +++ b/encfs/SSL_Cipher.cpp @@ -0,0 +1,816 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + * + */ + +#include "encfs.h" + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "SSL_Cipher.h" +#include "Range.h" +#include "MemoryPool.h" +#include "Mutex.h" + +#include + +#include + +#include +#include + +#include "i18n.h" + +using namespace std; +using namespace rel; +using namespace rlog; + +using boost::shared_ptr; +using boost::dynamic_pointer_cast; + +const int MAX_KEYLENGTH = 32; // in bytes (256 bit) +const int MAX_IVLENGTH = 16; +const int KEY_CHECKSUM_BYTES = 4; + +#ifndef MIN +inline int MIN(int a, int b) +{ + return (a < b) ? a : b; +} +#endif + +/* + This produces the same result as OpenSSL's EVP_BytesToKey. The difference + is that here we can explicitly specify the key size, instead of relying on + the state of EVP_CIPHER struct. EVP_BytesToKey will only produce 128 bit + keys for the EVP Blowfish interface, which is not what we want. + + Eliminated the salt code, since we don't use it.. Reason is that we're + using the derived key to encode random data. Since there is no known + plaintext, there is no ability for an attacker to pre-compute known + password->data mappings, which is what the salt is meant to frustrate. +*/ +int BytesToKey( int keyLen, int ivLen, const EVP_MD *md, + const unsigned char *data, int dataLen, + unsigned int rounds, unsigned char *key, unsigned char *iv) +{ + if( data == NULL || dataLen == 0 ) + return 0; // OpenSSL returns nkey here, but why? It is a failure.. + + unsigned char mdBuf[ EVP_MAX_MD_SIZE ]; + unsigned int mds=0; + int addmd =0; + int nkey = key ? keyLen : 0; + int niv = iv ? ivLen : 0; + + EVP_MD_CTX cx; + EVP_MD_CTX_init( &cx ); + + for(;;) + { + EVP_DigestInit_ex( &cx, md, NULL ); + if( addmd++ ) + EVP_DigestUpdate( &cx, mdBuf, mds ); + EVP_DigestUpdate( &cx, data, dataLen ); + EVP_DigestFinal_ex( &cx, mdBuf, &mds ); + + for(unsigned int i=1; i < rounds; ++i) + { + EVP_DigestInit_ex( &cx, md, NULL ); + EVP_DigestUpdate( &cx, mdBuf, mds ); + EVP_DigestFinal_ex( &cx, mdBuf, &mds ); + } + + int offset = 0; + int toCopy = MIN( nkey, mds - offset ); + if( toCopy ) + { + memcpy( key, mdBuf+offset, toCopy ); + key += toCopy; + nkey -= toCopy; + offset += toCopy; + } + toCopy = MIN( niv, mds - offset ); + if( toCopy ) + { + memcpy( iv, mdBuf+offset, toCopy ); + iv += toCopy; + niv -= toCopy; + offset += toCopy; + } + if((nkey == 0) && (niv == 0)) break; + } + EVP_MD_CTX_cleanup( &cx ); + OPENSSL_cleanse( mdBuf, sizeof(mdBuf) ); + + return keyLen; +} + +// - Version 1:0 used EVP_BytesToKey, which didn't do the right thing for +// Blowfish key lengths > 128 bit. +// - Version 2:0 uses BytesToKey. +// We support both 2:0 and 1:0, hence current:revision:age = 2:0:1 +// - Version 2:1 adds support for Message Digest function interface +static Interface BlowfishInterface( "ssl/blowfish", 2, 1, 1 ); +static Interface AESInterface( "ssl/aes", 2, 1, 1 ); + +#if defined(HAVE_EVP_BF) + +static Range BFKeyRange(128,256,32); +static Range BFBlockRange(64,4096,8); + +static shared_ptr NewBFCipher( const Interface &iface, int keyLen ) +{ + if( keyLen <= 0 ) + keyLen = 160; + + keyLen = BFKeyRange.closest( keyLen ); + + const EVP_CIPHER *blockCipher = EVP_bf_cbc(); + const EVP_CIPHER *streamCipher = EVP_bf_cfb(); + + return shared_ptr( new SSL_Cipher(iface, BlowfishInterface, + blockCipher, streamCipher, keyLen / 8) ); +} + +static bool BF_Cipher_registered = Cipher::Register("Blowfish", + // xgroup(setup) + gettext_noop("8 byte block cipher"), + BlowfishInterface, BFKeyRange, BFBlockRange, NewBFCipher); +#endif + + +#if defined(HAVE_EVP_AES) + +static Range AESKeyRange(128,256,64); +static Range AESBlockRange(64,4096,16); + +static shared_ptr NewAESCipher( const Interface &iface, int keyLen ) +{ + if( keyLen <= 0 ) + keyLen = 192; + + keyLen = AESKeyRange.closest( keyLen ); + + const EVP_CIPHER *blockCipher = 0; + const EVP_CIPHER *streamCipher = 0; + + switch(keyLen) + { + case 128: + blockCipher = EVP_aes_128_cbc(); + streamCipher = EVP_aes_128_cfb(); + break; + + case 192: + blockCipher = EVP_aes_192_cbc(); + streamCipher = EVP_aes_192_cfb(); + break; + + case 256: + default: + blockCipher = EVP_aes_256_cbc(); + streamCipher = EVP_aes_256_cfb(); + break; + } + + return shared_ptr( new SSL_Cipher(iface, AESInterface, + blockCipher, streamCipher, keyLen / 8) ); +} + +static bool AES_Cipher_registered = Cipher::Register("AES", + "16 byte block cipher", + AESInterface, AESKeyRange, AESBlockRange, NewAESCipher); +#endif + +class SSLKey : public AbstractCipherKey +{ +public: + pthread_mutex_t mutex; + + unsigned int keySize; // in bytes + unsigned int ivLength; + + // key data is first _keySize bytes, followed by _ivLength length bytes for + // iv + unsigned char *buffer; + + EVP_CIPHER_CTX block_enc; + EVP_CIPHER_CTX block_dec; + EVP_CIPHER_CTX stream_enc; + EVP_CIPHER_CTX stream_dec; + + HMAC_CTX mac_ctx; + + SSLKey(int keySize, int ivLength); + ~SSLKey(); +}; + +SSLKey::SSLKey(int keySize_, int ivLength_) +{ + this->keySize = keySize_; + this->ivLength = ivLength_; + pthread_mutex_init( &mutex, 0 ); + buffer = (unsigned char *)OPENSSL_malloc( keySize + ivLength ); + memset( buffer, 0, keySize + ivLength ); + // most likely fails unless we're running as root, or a user-page-lock + // kernel patch is applied.. + mlock( buffer, keySize + ivLength ); +} + +SSLKey::~SSLKey() +{ + memset( buffer, 0, keySize + ivLength ); + + OPENSSL_free( buffer ); + munlock( buffer, keySize + ivLength ); + + keySize = 0; + ivLength = 0; + buffer = 0; + + EVP_CIPHER_CTX_cleanup( &block_enc ); + EVP_CIPHER_CTX_cleanup( &block_dec ); + EVP_CIPHER_CTX_cleanup( &stream_enc ); + EVP_CIPHER_CTX_cleanup( &stream_dec ); + + HMAC_CTX_cleanup( &mac_ctx ); + + pthread_mutex_destroy( &mutex ); +} + + +inline unsigned char* KeyData( const shared_ptr &key ) +{ + return key->buffer; +} +inline unsigned char* IVData( const shared_ptr &key ) +{ + return key->buffer + key->keySize; +} + +void initKey(const shared_ptr &key, const EVP_CIPHER *_blockCipher, + const EVP_CIPHER *_streamCipher, int _keySize) +{ + Lock lock( key->mutex ); + // initialize the cipher context once so that we don't have to do it for + // every block.. + EVP_CIPHER_CTX_init( &key->block_enc ); + EVP_CIPHER_CTX_init( &key->block_dec ); + EVP_CIPHER_CTX_init( &key->stream_enc ); + EVP_CIPHER_CTX_init( &key->stream_dec ); + + EVP_EncryptInit_ex( &key->block_enc, _blockCipher, NULL, NULL, NULL); + EVP_DecryptInit_ex( &key->block_dec, _blockCipher, NULL, NULL, NULL); + EVP_EncryptInit_ex( &key->stream_enc, _streamCipher, NULL, NULL, NULL); + EVP_DecryptInit_ex( &key->stream_dec, _streamCipher, NULL, NULL, NULL); + + EVP_CIPHER_CTX_set_key_length( &key->block_enc, _keySize ); + EVP_CIPHER_CTX_set_key_length( &key->block_dec, _keySize ); + EVP_CIPHER_CTX_set_key_length( &key->stream_enc, _keySize ); + EVP_CIPHER_CTX_set_key_length( &key->stream_dec, _keySize ); + + EVP_CIPHER_CTX_set_padding( &key->block_enc, 0 ); + EVP_CIPHER_CTX_set_padding( &key->block_dec, 0 ); + EVP_CIPHER_CTX_set_padding( &key->stream_enc, 0 ); + EVP_CIPHER_CTX_set_padding( &key->stream_dec, 0 ); + + EVP_EncryptInit_ex( &key->block_enc, NULL, NULL, KeyData(key), NULL); + EVP_DecryptInit_ex( &key->block_dec, NULL, NULL, KeyData(key), NULL); + EVP_DecryptInit_ex( &key->stream_enc, NULL, NULL, KeyData(key), NULL); + EVP_DecryptInit_ex( &key->stream_dec, NULL, NULL, KeyData(key), NULL); + + HMAC_CTX_init( &key->mac_ctx ); + HMAC_Init_ex( &key->mac_ctx, KeyData(key), _keySize, EVP_sha1(), 0 ); +} + + +static RLogChannel * CipherInfo = DEF_CHANNEL( "info/cipher", Log_Info ); + +SSL_Cipher::SSL_Cipher(const Interface &iface_, + const Interface &realIface_, + const EVP_CIPHER *blockCipher, + const EVP_CIPHER *streamCipher, + int keySize_) +{ + this->iface = iface_; + this->realIface = realIface_; + this->_blockCipher = blockCipher; + this->_streamCipher = streamCipher; + this->_keySize = keySize_; + this->_ivLength = EVP_CIPHER_iv_length( _blockCipher ); + + rAssert(_ivLength == 8 || _ivLength == 16); + + rLog(CipherInfo, "allocated cipher %s, keySize %i, ivlength %i", + iface.name().c_str(), _keySize, _ivLength); + + if( (EVP_CIPHER_key_length( _blockCipher ) != (int )_keySize) + && iface.current() == 1) + { + rWarning("Running in backward compatibilty mode for 1.0 - \n" + "key is really %i bits, not %i.\n" + "Consider creating a new filesystem and moving your data.", + EVP_CIPHER_key_length( _blockCipher ) * 8, + _keySize * 8 ); + } +} + +SSL_Cipher::~SSL_Cipher() +{ +} + +Interface SSL_Cipher::interface() const +{ + return realIface; +} + +/* + create a key from the password. + Use SHA to distribute entropy from the password into the key. + + This algorithm must remain constant for backward compatibility, as this key + is used to encipher/decipher the master key. +*/ +CipherKey SSL_Cipher::newKey(const char *password, int passwdLength) +{ + const EVP_MD *md = EVP_sha1(); + if(!md) + { + rError("Unknown digest SHA1"); + return CipherKey(); + } + + shared_ptr key( new SSLKey( _keySize, _ivLength) ); + + int bytes = 0; + if( iface.current() > 1 ) + { + // now we use BytesToKey, which can deal with Blowfish keys larger then + // 128 bits. + bytes = BytesToKey( _keySize, _ivLength, EVP_sha1(), + (unsigned char *)password, passwdLength, 16, + KeyData(key), IVData(key) ); + + // the reason for moving from EVP_BytesToKey to BytesToKey function.. + if(bytes != (int)_keySize) + { + rWarning("newKey: BytesToKey returned %i, expecting %i key bytes", + bytes, _keySize); + } + } else + { + // for backward compatibility with filesystems created with 1:0 + bytes = EVP_BytesToKey( _blockCipher, EVP_sha1(), NULL, + (unsigned char *)password, passwdLength, 16, + KeyData(key), IVData(key) ); + } + + initKey( key, _blockCipher, _streamCipher, _keySize ); + + return key; +} + +/* + Create a random key. + We use the OpenSSL library to generate random bytes, then take the hash of + those bytes to use as the key. + + This algorithm can change at any time without affecting backward + compatibility. +*/ +CipherKey SSL_Cipher::newRandomKey() +{ + const int bufLen = MAX_KEYLENGTH; + unsigned char tmpBuf[ bufLen ]; + // to avoid warnings of uninitialized data from valgrind + memset(tmpBuf, 0, sizeof(tmpBuf)); + if(RAND_bytes( tmpBuf, bufLen ) == 0) + { + char errStr[120]; // specs require string at least 120 bytes long.. + unsigned long errVal = 0; + if((errVal = ERR_get_error()) != 0) + { + rWarning("openssl error: %s", ERR_error_string( errVal, errStr )); + return CipherKey(); + } + } + + shared_ptr key( new SSLKey( _keySize, _ivLength) ); + + // doesn't need to be versioned, because a random key is a random key.. + // Doesn't need to be reproducable.. + int bytes = BytesToKey( _keySize, _ivLength, EVP_sha1(), tmpBuf, + bufLen, 16, KeyData(key), IVData(key) ); + if(bytes != (int)_keySize) + { + rWarning("newKey: BytesToKey returned %i, expecting %i key bytes", + bytes, _keySize); + } + + memset( tmpBuf, 0, bufLen ); + + initKey( key, _blockCipher, _streamCipher, _keySize ); + + return key; +} + +/* + compute a 64-bit check value for the data using HMAC. +*/ +static uint64_t _checksum_64( SSLKey *key, + const unsigned char *data, int dataLen, uint64_t *chainedIV) +{ + rAssert( dataLen > 0 ); + Lock lock( key->mutex ); + + unsigned char md[EVP_MAX_MD_SIZE]; + unsigned int mdLen = EVP_MAX_MD_SIZE; + + HMAC_Init_ex( &key->mac_ctx, 0, 0, 0, 0 ); + HMAC_Update( &key->mac_ctx, data, dataLen ); + if(chainedIV) + { + // toss in the chained IV as well + uint64_t tmp = *chainedIV; + unsigned char h[8]; + for(unsigned int i=0; i<8; ++i) + { + h[i] = tmp & 0xff; + tmp >>= 8; + } + + HMAC_Update( &key->mac_ctx, h, 8 ); + } + + HMAC_Final( &key->mac_ctx, md, &mdLen ); + + rAssert(mdLen != 0); + + // chop this down to a 64bit value.. + unsigned char h[8] = {0,0,0,0,0,0,0,0}; + for(unsigned int i=0; i<(mdLen-1); ++i) + h[i%8] ^= (unsigned char)(md[i]); + + uint64_t value = (uint64_t)h[0]; + for(int i=1; i<8; ++i) + value = (value << 8) | (uint64_t)h[i]; + + return value; +} + +void SSL_Cipher::randomize( unsigned char *buf, int len ) const +{ + memset(buf, 0, len); + int result = RAND_pseudo_bytes( buf, len ); + rAssert( result >= 0 ); +} + +uint64_t SSL_Cipher::MAC_64( const unsigned char *data, int len, + const CipherKey &key, uint64_t *chainedIV ) const +{ + shared_ptr mk = dynamic_pointer_cast(key); + uint64_t tmp = _checksum_64( mk.get(), data, len, chainedIV ); + + if(chainedIV) + *chainedIV = tmp; + + return tmp; +} + +CipherKey SSL_Cipher::readKey(const unsigned char *data, + const CipherKey &masterKey, bool checkKey) +{ + shared_ptr mk = dynamic_pointer_cast(masterKey); + rAssert(mk->keySize == _keySize); + + unsigned char tmpBuf[ MAX_KEYLENGTH + MAX_IVLENGTH ]; + + // First N bytes are checksum bytes. + unsigned int checksum = 0; + for(int i=0; i key( new SSLKey( _keySize, _ivLength) ); + + memcpy( key->buffer, tmpBuf, _keySize + _ivLength ); + memset( tmpBuf, 0, sizeof(tmpBuf) ); + + initKey( key, _blockCipher, _streamCipher, _keySize ); + + return key; +} + +void SSL_Cipher::writeKey(const CipherKey &ckey, unsigned char *data, + const CipherKey &masterKey) +{ + shared_ptr key = dynamic_pointer_cast(ckey); + rAssert(key->keySize == _keySize); + rAssert(key->ivLength == _ivLength); + + shared_ptr mk = dynamic_pointer_cast(masterKey); + rAssert(mk->keySize == _keySize); + rAssert(mk->ivLength == _ivLength); + + unsigned char tmpBuf[ MAX_KEYLENGTH + MAX_IVLENGTH ]; + + int bufLen = _keySize + _ivLength; + memcpy( tmpBuf, key->buffer, bufLen ); + + unsigned int checksum = MAC_32( tmpBuf, bufLen, masterKey ); + + streamEncode(tmpBuf, bufLen, checksum, masterKey); + memcpy( data+KEY_CHECKSUM_BYTES, tmpBuf, bufLen ); + + // first N bytes contain HMAC derived checksum.. + for(int i=1; i<=KEY_CHECKSUM_BYTES; ++i) + { + data[KEY_CHECKSUM_BYTES-i] = checksum & 0xff; + checksum >>= 8; + } + + memset( tmpBuf, 0, sizeof(tmpBuf) ); +} + +bool SSL_Cipher::compareKey( const CipherKey &A, const CipherKey &B) const +{ + shared_ptr key1 = dynamic_pointer_cast(A); + shared_ptr key2 = dynamic_pointer_cast(B); + + rAssert(key1->keySize == _keySize); + rAssert(key2->keySize == _keySize); + + if(memcmp(key1->buffer, key2->buffer, _keySize + _ivLength) != 0) + return false; + else + return true; +} + +int SSL_Cipher::encodedKeySize() const +{ + return _keySize + _ivLength + KEY_CHECKSUM_BYTES; +} + +int SSL_Cipher::keySize() const +{ + return _keySize; +} + +int SSL_Cipher::cipherBlockSize() const +{ + return EVP_CIPHER_block_size( _blockCipher ); +} + +void SSL_Cipher::setIVec( unsigned char *ivec, unsigned int seed, + const shared_ptr &key) const +{ + /* These multiplication constants chosen as they represent (non optimal) + Golumb rulers, the idea being to spread around the information in the + seed. + + 0x060a4011 : ruler length 26, 7 marks, 21 measurable lengths + 0x0221040d : ruler length 25, 7 marks, 21 measurable lengths + */ + unsigned int var1 = 0x060a4011 * seed; + unsigned int var2 = 0x0221040d * (seed ^ 0xD3FEA11C); + + memcpy( ivec, IVData(key), _ivLength ); + + ivec[0] ^= (var1 >> 24) & 0xff; + ivec[1] ^= (var2 >> 16) & 0xff; + ivec[2] ^= (var1 >> 8 ) & 0xff; + ivec[3] ^= (var2 ) & 0xff; + ivec[4] ^= (var2 >> 24) & 0xff; + ivec[5] ^= (var1 >> 16) & 0xff; + ivec[6] ^= (var2 >> 8 ) & 0xff; + ivec[7] ^= (var1 ) & 0xff; + + if(_ivLength > 8) + { + ivec[8+0] ^= (var1 ) & 0xff; + ivec[8+1] ^= (var2 >> 8 ) & 0xff; + ivec[8+2] ^= (var1 >> 16) & 0xff; + ivec[8+3] ^= (var2 >> 24) & 0xff; + ivec[8+4] ^= (var1 >> 24) & 0xff; + ivec[8+5] ^= (var2 >> 16) & 0xff; + ivec[8+6] ^= (var1 >> 8 ) & 0xff; + ivec[8+7] ^= (var2 ) & 0xff; + } +} + +static void flipBytes(unsigned char *buf, int size) +{ + unsigned char revBuf[64]; + + int bytesLeft = size; + while(bytesLeft) + { + int toFlip = MIN( sizeof(revBuf), bytesLeft ); + + for(int i=0; i 0 ); + shared_ptr key = dynamic_pointer_cast(ckey); + rAssert(key->keySize == _keySize); + rAssert(key->ivLength == _ivLength); + + Lock lock( key->mutex ); + + unsigned char ivec[ MAX_IVLENGTH ]; + int dstLen=0, tmpLen=0; + + shuffleBytes( buf, size ); + + setIVec( ivec, iv64, key ); + EVP_EncryptInit_ex( &key->stream_enc, NULL, NULL, NULL, ivec); + EVP_EncryptUpdate( &key->stream_enc, buf, &dstLen, buf, size ); + EVP_EncryptFinal_ex( &key->stream_enc, buf+dstLen, &tmpLen ); + + flipBytes( buf, size ); + shuffleBytes( buf, size ); + + setIVec( ivec, iv64 + 1, key ); + EVP_EncryptInit_ex( &key->stream_enc, NULL, NULL, NULL, ivec); + EVP_EncryptUpdate( &key->stream_enc, buf, &dstLen, buf, size ); + EVP_EncryptFinal_ex( &key->stream_enc, buf+dstLen, &tmpLen ); + + dstLen += tmpLen; + if(dstLen != size) + { + rError("encoding %i bytes, got back %i (%i in final_ex)", + size, dstLen, tmpLen); + } + + return true; +} + +bool SSL_Cipher::streamDecode(unsigned char *buf, int size, + uint64_t iv64, const CipherKey &ckey) const +{ + rAssert( size > 0 ); + shared_ptr key = dynamic_pointer_cast(ckey); + rAssert(key->keySize == _keySize); + rAssert(key->ivLength == _ivLength); + + Lock lock( key->mutex ); + + unsigned char ivec[ MAX_IVLENGTH ]; + int dstLen=0, tmpLen=0; + + setIVec( ivec, iv64 + 1, key ); + EVP_DecryptInit_ex( &key->stream_dec, NULL, NULL, NULL, ivec); + EVP_DecryptUpdate( &key->stream_dec, buf, &dstLen, buf, size ); + EVP_DecryptFinal_ex( &key->stream_dec, buf+dstLen, &tmpLen ); + + unshuffleBytes( buf, size ); + flipBytes( buf, size ); + + setIVec( ivec, iv64, key ); + EVP_DecryptInit_ex( &key->stream_dec, NULL, NULL, NULL, ivec); + EVP_DecryptUpdate( &key->stream_dec, buf, &dstLen, buf, size ); + EVP_DecryptFinal_ex( &key->stream_dec, buf+dstLen, &tmpLen ); + + unshuffleBytes( buf, size ); + + dstLen += tmpLen; + if(dstLen != size) + { + rError("encoding %i bytes, got back %i (%i in final_ex)", + size, dstLen, tmpLen); + } + + return true; +} + + +bool SSL_Cipher::blockEncode(unsigned char *buf, int size, + uint64_t iv64, const CipherKey &ckey ) const +{ + rAssert( size > 0 ); + shared_ptr key = dynamic_pointer_cast(ckey); + rAssert(key->keySize == _keySize); + rAssert(key->ivLength == _ivLength); + + // data must be integer number of blocks + const int blockMod = size % EVP_CIPHER_CTX_block_size( &key->block_enc ); + if(blockMod != 0) + throw ERROR("Invalid data size, not multiple of block size"); + + Lock lock( key->mutex ); + + unsigned char ivec[ MAX_IVLENGTH ]; + + int dstLen = 0, tmpLen = 0; + setIVec( ivec, iv64, key ); + + EVP_EncryptInit_ex( &key->block_enc, NULL, NULL, NULL, ivec); + EVP_EncryptUpdate( &key->block_enc, buf, &dstLen, buf, size ); + EVP_EncryptFinal_ex( &key->block_enc, buf+dstLen, &tmpLen ); + dstLen += tmpLen; + + if(dstLen != size) + { + rError("encoding %i bytes, got back %i (%i in final_ex)", + size, dstLen, tmpLen); + } + + return true; +} + +bool SSL_Cipher::blockDecode(unsigned char *buf, int size, + uint64_t iv64, const CipherKey &ckey ) const +{ + rAssert( size > 0 ); + shared_ptr key = dynamic_pointer_cast(ckey); + rAssert(key->keySize == _keySize); + rAssert(key->ivLength == _ivLength); + + // data must be integer number of blocks + const int blockMod = size % EVP_CIPHER_CTX_block_size( &key->block_dec ); + if(blockMod != 0) + throw ERROR("Invalid data size, not multiple of block size"); + + Lock lock( key->mutex ); + + unsigned char ivec[ MAX_IVLENGTH ]; + + int dstLen = 0, tmpLen = 0; + setIVec( ivec, iv64, key ); + + EVP_DecryptInit_ex( &key->block_dec, NULL, NULL, NULL, ivec); + EVP_DecryptUpdate( &key->block_dec, buf, &dstLen, buf, size ); + EVP_DecryptFinal_ex( &key->block_dec, buf+dstLen, &tmpLen ); + dstLen += tmpLen; + + if(dstLen != size) + { + rError("decoding %i bytes, got back %i (%i in final_ex)", + size, dstLen, tmpLen); + } + + return true; +} + +bool SSL_Cipher::Enabled() +{ + return true; +} + diff --git a/encfs/SSL_Cipher.h b/encfs/SSL_Cipher.h new file mode 100644 index 0000000..664c0f4 --- /dev/null +++ b/encfs/SSL_Cipher.h @@ -0,0 +1,141 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + * + */ + +#ifndef _SSL_Cipher_incl_ +#define _SSL_Cipher_incl_ + +#include "Cipher.h" +#include "Interface.h" + +struct SSLKey; +#ifndef EVP_CIPHER +struct evp_cipher_st; +typedef struct evp_cipher_st EVP_CIPHER; +#endif + +using boost::shared_ptr; + +/* + Implements Cipher interface for OpenSSL's ciphers. + + Design: + Variable algorithm, key size, and block size. + + Partial blocks, keys, and names are encrypted using the cipher in a pseudo + stream mode (CFB). + + Keys are encrypted with 2-4 (KEY_CHECKSUM_BYTES define) checksum bytes + derived from an HMAC over both they key data and the initial value vector + associated with the key. This allows a good chance at detecting an + incorrect password when we try and decrypt the master key. + + File names are encrypted in the same way, with 2 checksum bytes derived + from an HMAC over the filename. This is done not to allow checking the + results, but to make the output much more random. Changing one letter in a + filename should result in a completely different encrypted filename, to + help frustrate any attempt to guess information about files from their + encrypted names. + + Stream encryption involves two encryption passes over the data, implemented + as: + 1. shuffle + 2. encrypt + 3. reverse + 4. shuffle + 5. encrypt + The reason for the shuffle and reverse steps (and the second encrypt pass) + is to try and propogate any changed bits to a larger set. If only a single + pass was made with the stream cipher in CFB mode, then a change to one byte + may only affect one byte of output, allowing some XOR attacks. + + The shuffle/encrypt is used as above in filename encryption as well, + although it is not necessary as they have checksum bytes which augment the + initial value vector to randomize the output. But it makes the code + simpler to reuse the encryption algorithm as is. +*/ +class SSL_Cipher : public Cipher +{ + rel::Interface iface; + rel::Interface realIface; + const EVP_CIPHER *_blockCipher; + const EVP_CIPHER *_streamCipher; + unsigned int _keySize; // in bytes + unsigned int _ivLength; + +public: + SSL_Cipher(const rel::Interface &iface, const rel::Interface &realIface, + const EVP_CIPHER *blockCipher, const EVP_CIPHER *streamCipher, + int keyLength); + virtual ~SSL_Cipher(); + + // returns the real interface, not the one we're emulating (if any).. + virtual rel::Interface interface() const; + + // create a new key based on a password + virtual CipherKey newKey(const char *password, int passwdLength); + // create a new random key + virtual CipherKey newRandomKey(); + + // data must be len keySize() + virtual CipherKey readKey(const unsigned char *data, + const CipherKey &encodingKey, + bool checkKey); + virtual void writeKey(const CipherKey &key, unsigned char *data, + const CipherKey &encodingKey); + virtual bool compareKey( const CipherKey &A, + const CipherKey &B ) const; + + // meta-data about the cypher + virtual int keySize() const; + virtual int encodedKeySize() const; + virtual int cipherBlockSize() const; + + virtual void randomize( unsigned char *buf, int len ) const; + + virtual uint64_t MAC_64( const unsigned char *src, int len, + const CipherKey &key, uint64_t *augment ) const; + + // functional interfaces + /* + Stream encoding in-place. + */ + virtual bool streamEncode(unsigned char *in, int len, + uint64_t iv64, const CipherKey &key) const; + virtual bool streamDecode(unsigned char *in, int len, + uint64_t iv64, const CipherKey &key) const; + + /* + Block encoding is done in-place. Partial blocks are supported, but + blocks are always expected to begin on a block boundary. See + blockSize(). + */ + virtual bool blockEncode(unsigned char *buf, int size, + uint64_t iv64, const CipherKey &key) const; + virtual bool blockDecode(unsigned char *buf, int size, + uint64_t iv64, const CipherKey &key) const; + + // hack to help with static builds + static bool Enabled(); +private: + void setIVec( unsigned char *ivec, unsigned int seed, + const shared_ptr &key ) const; +}; + + +#endif + diff --git a/encfs/StreamNameIO.cpp b/encfs/StreamNameIO.cpp new file mode 100644 index 0000000..c584be6 --- /dev/null +++ b/encfs/StreamNameIO.cpp @@ -0,0 +1,205 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + */ + +#include "StreamNameIO.h" + +#include "Cipher.h" +#include "base64.h" + +#include +#include + +#include "i18n.h" + +using namespace rel; +using namespace std; + +static shared_ptr NewStreamNameIO( const Interface &iface, + const shared_ptr &cipher, const CipherKey &key) +{ + return shared_ptr( new StreamNameIO( iface, cipher, key ) ); +} + +static bool StreamIO_registered = NameIO::Register("Stream", + gettext_noop("Stream encoding, keeps filenames as short as possible"), + StreamNameIO::CurrentInterface(), + NewStreamNameIO); + + +/* + - Version 0.1 is for EncFS 0.x support. The difference to 1.0 is that 0.x + stores the file checksums at the end of the encoded name, where 1.0 + stores them at the beginning. + + - Version 1.0 is the basic stream encoding mode used since the beginning of + EncFS. There is a slight difference in filename encodings from EncFS 0.x + to 1.0.x. This implements just the 1.0.x method. + + - Version 1.1 adds support for IV chaining. This is transparently + backward compatible, since older filesystems do not use IV chaining. + + - Version 2.0 uses full 64 bit IV during IV chaining mode. Prior versions + used only the 16 bit output from MAC_16. This reduces the theoretical + possibility (unlikely to make any difference in practice) of two files + with the same name in different directories ending up with the same + encrypted name. Added because there is no good reason to chop to 16 + bits. + + - Version 2.1 adds support for version 0 for EncFS 0.x compatibility. +*/ +Interface StreamNameIO::CurrentInterface() +{ + // implement major version 2, 1, and 0 + return Interface("nameio/stream", 2, 1, 2); +} + +StreamNameIO::StreamNameIO( const rel::Interface &iface, + const shared_ptr &cipher, + const CipherKey &key ) + : _interface( iface.current() ) + , _cipher( cipher ) + , _key( key ) +{ + +} + +StreamNameIO::~StreamNameIO() +{ +} + +Interface StreamNameIO::interface() const +{ + return CurrentInterface(); +} + +int StreamNameIO::maxEncodedNameLen( int plaintextStreamLen ) const +{ + int encodedStreamLen = 2 + plaintextStreamLen; + return B256ToB64Bytes( encodedStreamLen ); +} + +int StreamNameIO::maxDecodedNameLen( int encodedStreamLen ) const +{ + int decLen256 = B64ToB256Bytes( encodedStreamLen ); + return decLen256 - 2; +} + +int StreamNameIO::encodeName( const char *plaintextName, int length, + uint64_t *iv, char *encodedName ) const +{ + uint64_t tmpIV = 0; + if( iv && _interface >= 2 ) + tmpIV = *iv; + + unsigned int mac = _cipher->MAC_16( (const unsigned char *)plaintextName, + length, _key, iv ); + + // add on checksum bytes + unsigned char *encodeBegin; + if(_interface >= 1) + { + // current versions store the checksum at the beginning + encodedName[0] = (mac >> 8) & 0xff; + encodedName[1] = (mac ) & 0xff; + encodeBegin = (unsigned char *)encodedName+2; + } else + { + // encfs 0.x stored checksums at the end. + encodedName[length] = (mac >> 8) & 0xff; + encodedName[length+1] = (mac ) & 0xff; + encodeBegin = (unsigned char *)encodedName; + } + + // stream encode the plaintext bytes + memcpy( encodeBegin, plaintextName, length ); + _cipher->nameEncode( encodeBegin, length, (uint64_t)mac ^ tmpIV, _key); + + // convert the entire thing to base 64 ascii.. + int encodedStreamLen = length + 2; + int encLen64 = B256ToB64Bytes( encodedStreamLen ); + + changeBase2Inline( (unsigned char *)encodedName, encodedStreamLen, + 8, 6, true ); + B64ToAscii( (unsigned char *)encodedName, encLen64 ); + + return encLen64; +} + +int StreamNameIO::decodeName( const char *encodedName, int length, + uint64_t *iv, char *plaintextName ) const +{ + rAssert(length > 2); + int decLen256 = B64ToB256Bytes( length ); + int decodedStreamLen = decLen256 - 2; + + if(decodedStreamLen <= 0) + throw ERROR("Filename too small to decode"); + + BUFFER_INIT( tmpBuf, 32, (unsigned int)length ); + + // decode into tmpBuf, because this step produces more data then we can fit + // into the result buffer.. + AsciiToB64( (unsigned char *)tmpBuf, (unsigned char *)encodedName, length ); + changeBase2Inline((unsigned char *)tmpBuf, length, 6, 8, false); + + // pull out the checksum value which is used as an initialization vector + uint64_t tmpIV = 0; + unsigned int mac; + if(_interface >= 1) + { + // current versions store the checksum at the beginning + mac = ((unsigned int)((unsigned char)tmpBuf[0])) << 8 + | ((unsigned int)((unsigned char)tmpBuf[1])); + + // version 2 adds support for IV chaining.. + if( iv && _interface >= 2 ) + tmpIV = *iv; + + memcpy( plaintextName, tmpBuf+2, decodedStreamLen ); + } else + { + // encfs 0.x stored checksums at the end. + mac = ((unsigned int)((unsigned char)tmpBuf[decodedStreamLen])) << 8 + | ((unsigned int)((unsigned char)tmpBuf[decodedStreamLen+1])); + + memcpy( plaintextName, tmpBuf, decodedStreamLen ); + } + + // use nameDeocde instead of streamDecode for backward compatibility + _cipher->nameDecode( (unsigned char *)plaintextName, decodedStreamLen, + (uint64_t)mac ^ tmpIV, _key); + + // compute MAC to check with stored value + unsigned int mac2 = _cipher->MAC_16((const unsigned char *)plaintextName, + decodedStreamLen, _key, iv); + + BUFFER_RESET( tmpBuf ); + if(mac2 != mac) + { + rDebug("checksum mismatch: expected %u, got %u", mac, mac2); + rDebug("on decode of %i bytes", decodedStreamLen); + throw ERROR( "checksum mismatch in filename decode" ); + } + + return decodedStreamLen; +} + +bool StreamNameIO::Enabled() +{ + return true; +} + diff --git a/encfs/StreamNameIO.h b/encfs/StreamNameIO.h new file mode 100644 index 0000000..38e4be8 --- /dev/null +++ b/encfs/StreamNameIO.h @@ -0,0 +1,57 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + */ + +#ifndef _StreamNameIO_incl_ +#define _StreamNameIO_incl_ + +#include "NameIO.h" +#include "CipherKey.h" + +class Cipher; +using boost::shared_ptr; + +class StreamNameIO : public NameIO +{ +public: + static rel::Interface CurrentInterface(); + + StreamNameIO( const rel::Interface &iface, + const shared_ptr &cipher, + const CipherKey &key ); + virtual ~StreamNameIO(); + + virtual rel::Interface interface() const; + + virtual int maxEncodedNameLen( int plaintextNameLen ) const; + virtual int maxDecodedNameLen( int encodedNameLen ) const; + + // hack to help with static builds + static bool Enabled(); +protected: + virtual int encodeName( const char *plaintextName, int length, + uint64_t *iv, char *encodedName ) const; + virtual int decodeName( const char *encodedName, int length, + uint64_t *iv, char *plaintextName ) const; +private: + int _interface; + shared_ptr _cipher; + CipherKey _key; +}; + + +#endif + diff --git a/encfs/base64.cpp b/encfs/base64.cpp new file mode 100644 index 0000000..f86bc24 --- /dev/null +++ b/encfs/base64.cpp @@ -0,0 +1,164 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2002-2004, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + * + */ + +#include "base64.h" + +// change between two powers of two, stored as the low bits of the bytes in the +// arrays. +// It is the caller's responsibility to make sure the output array is large +// enough. +void changeBase2(unsigned char *src, int srcLen, int src2Pow, + unsigned char *dst, int dstLen, int dst2Pow) +{ + unsigned long work = 0; + int workBits = 0; // number of bits left in the work buffer + unsigned char *end = src + srcLen; + unsigned char *origDst = dst; + const int mask = (1 << dst2Pow) -1; + + // copy the new bits onto the high bits of the stream. + // The bits that fall off the low end are the output bits. + while(src != end) + { + work |= ((unsigned long)(*src++)) << workBits; + workBits += src2Pow; + + while(workBits >= dst2Pow) + { + *dst++ = work & mask; + work >>= dst2Pow; + workBits -= dst2Pow; + } + } + + // now, we could have a partial value left in the work buffer.. + if(workBits && ((dst - origDst) < dstLen)) + *dst++ = work & mask; +} + +/* + Same as changeBase2, except the output is written over the input data. The + output is assumed to be large enough to accept the data. + + Uses the stack to store output values. Recurse every time a new value is + to be written, then write the value at the tail end of the recursion. +*/ +static +void changeBase2Inline(unsigned char *src, int srcLen, + int src2Pow, int dst2Pow, + bool outputPartialLastByte, + unsigned long work, + int workBits, + unsigned char *outLoc) +{ + const int mask = (1 << dst2Pow) -1; + if(!outLoc) + outLoc = src; + + // copy the new bits onto the high bits of the stream. + // The bits that fall off the low end are the output bits. + while(srcLen && workBits < dst2Pow) + { + work |= ((unsigned long)(*src++)) << workBits; + workBits += src2Pow; + --srcLen; + } + + // we have at least one value that can be output + char outVal = work & mask; + work >>= dst2Pow; + workBits -= dst2Pow; + + if(srcLen) + { + // more input left, so recurse + changeBase2Inline( src, srcLen, src2Pow, dst2Pow, + outputPartialLastByte, work, workBits, outLoc+1); + *outLoc = outVal; + } else + { + // no input left, we can write remaining values directly + *outLoc++ = outVal; + + // we could have a partial value left in the work buffer.. + if(workBits && outputPartialLastByte) + *outLoc = work & mask; + } +} + +void changeBase2Inline(unsigned char *src, int srcLen, + int src2Pow, int dst2Pow, + bool outputPartialLastByte) +{ + changeBase2Inline(src, srcLen, src2Pow, dst2Pow, + outputPartialLastByte, 0, 0, 0); +} + + +// character set for ascii b64: +// ",-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +// a standard base64 (eg a64l doesn't use ',-' but uses './'. We don't +// do that because '/' is a reserved character, and it is useful not to have +// '.' included in the encrypted names, so that it can be reserved for files +// with special meaning. +static const char B642AsciiTable[] = ",-0123456789"; +void B64ToAscii(unsigned char *in, int length) +{ + for(int offset=0; offset 11) + { + if(ch > 37) + ch += 'a' - 38; + else + ch += 'A' - 12; + } else + ch = B642AsciiTable[ ch ]; + + in[offset] = ch; + } +} + +static const unsigned char Ascii2B64Table[] = + " 01 23456789:; "; + // 0123456789 123456789 123456789 123456789 123456789 123456789 1234 + // 0 1 2 3 4 5 6 +void AsciiToB64(unsigned char *in, int length) +{ + return AsciiToB64(in, in, length); +} + +void AsciiToB64(unsigned char *out, const unsigned char *in, int length) +{ + while(length--) + { + unsigned char ch = *in++; + if(ch >= 'A') + { + if(ch >= 'a') + ch += 38 - 'a'; + else + ch += 12 - 'A'; + } else + ch = Ascii2B64Table[ ch ] - '0'; + + *out++ = ch; + } +} + diff --git a/encfs/base64.h b/encfs/base64.h new file mode 100644 index 0000000..7c7b911 --- /dev/null +++ b/encfs/base64.h @@ -0,0 +1,55 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2002-2003, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + * + */ + +#ifndef _base64_incl_ +#define _base64_incl_ + + +inline int B64ToB256Bytes( int numB64Bytes ) +{ + return (numB64Bytes * 6) / 8; // round down +} + +inline int B256ToB64Bytes( int numB256Bytes ) +{ + return (numB256Bytes * 8 + 5) / 6; // round up +} + + +/* + convert data between different bases - each being a power of 2. +*/ +void changeBase2(unsigned char *src, int srcLength, int srcPow2, + unsigned char *dst, int dstLength, int dstPow2); + +/* + same as changeBase2, but writes output over the top of input data. +*/ +void changeBase2Inline(unsigned char *buf, int srcLength, + int srcPow2, int dst2Pow, + bool outputPartialLastByte); + + +// inplace translation from values [0,2^6] => base64 ASCII +void B64ToAscii(unsigned char *buf, int length); +// inplace translation from values base64 ASCII => [0,2^6] +void AsciiToB64(unsigned char *buf, int length); +void AsciiToB64(unsigned char *out, const unsigned char *in, int length); + +#endif + diff --git a/encfs/docs/Makefile.am b/encfs/docs/Makefile.am new file mode 100644 index 0000000..271bd41 --- /dev/null +++ b/encfs/docs/Makefile.am @@ -0,0 +1,4 @@ +####### kdevelop will overwrite this part!!! (begin)########## + + +####### kdevelop will overwrite this part!!! (end)############ diff --git a/encfs/docs/en/Makefile.am b/encfs/docs/en/Makefile.am new file mode 100644 index 0000000..271bd41 --- /dev/null +++ b/encfs/docs/en/Makefile.am @@ -0,0 +1,4 @@ +####### kdevelop will overwrite this part!!! (begin)########## + + +####### kdevelop will overwrite this part!!! (end)############ diff --git a/encfs/encfs.cpp b/encfs/encfs.cpp new file mode 100644 index 0000000..6e4e5c8 --- /dev/null +++ b/encfs/encfs.cpp @@ -0,0 +1,765 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2003-2007, Valient Gough + * + * This program is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include "encfs.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#ifdef linux +#include +#endif + +#ifdef HAVE_ATTR_XATTR_H +#include +#elif HAVE_SYS_XATTR_H +#include +#endif + +#ifdef HAVE_ULOCKMGR_H +extern "C" { +#include +} +#endif + + +#include +#include + +#include + +#include "DirNode.h" +#include "MemoryPool.h" +#include "FileUtils.h" +#include "Mutex.h" +#include "Context.h" + +#include +#include + +#ifndef MIN +#define MIN(a,b) (((a)<(b)) ? (a): (b)) +#endif + +#define ESUCCESS 0 + +using namespace std; +using namespace rlog; +using rel::Lock; +using namespace boost; + +#define GET_FN(ctx, finfo) ctx->getNode((void*)(uintptr_t)finfo->fh) + +static RLogChannel *Info = DEF_CHANNEL("info", Log_Info); + +static EncFS_Context * context() +{ + return (EncFS_Context*)fuse_get_context()->private_data; +} + +// helper function -- apply a functor to a cipher path, given the plain path +template +static int withCipherPath( const char *opName, const char *path, + int (*op)(EncFS_Context *, const string &name, T data ), T data ) +{ + EncFS_Context *ctx = context(); + + int res = -EIO; + shared_ptr FSRoot = ctx->getRoot(&res); + if(!FSRoot) + return res; + + try + { + string cyName = FSRoot->cipherPath( path ); + rLog(Info, "%s %s", opName, cyName.c_str()); + + res = op( ctx, cyName, data ); + + if(res == -1) + { + int eno = errno; + rInfo("%s error: %s", opName, strerror(eno)); + res = -eno; + } else + res = ESUCCESS; + } catch( rlog::Error &err ) + { + rError("error caught in %s", opName); + err.log( _RLWarningChannel ); + } + return res; +} + +// helper function -- apply a functor to a node +template +static int withFileNode( const char *opName, + const char *path, struct fuse_file_info *fi, + int (*op)(FileNode *, T data ), T data ) +{ + EncFS_Context *ctx = context(); + + int res = -EIO; + shared_ptr FSRoot = ctx->getRoot(&res); + if(!FSRoot) + return res; + + try + { + shared_ptr fnode; + + if(fi != NULL) + fnode = GET_FN(ctx, fi); + else + fnode = FSRoot->lookupNode( path, opName ); + + rAssert(fnode != NULL); + rLog(Info, "%s %s", opName, fnode->cipherName()); + res = op( fnode.get(), data ); + + if(res < 0) + rInfo("%s error: %s", opName, strerror(-res)); + } catch( rlog::Error &err ) + { + rError("error caught in %s", opName); + err.log( _RLWarningChannel ); + } + return res; +} + +/* + The rLog messages below always print out encrypted filenames, not + plaintext. The reason is so that it isn't possible to leak information + about the encrypted data through rlog interfaces. + + + The purpose of this layer of code is to take the FUSE request and dispatch + to the internal interfaces. Any marshaling of arguments and return types + can be done here. +*/ + +int _do_getattr(FileNode *fnode, struct stat *stbuf) +{ + return fnode->getAttr(stbuf); +} + +int encfs_getattr(const char *path, struct stat *stbuf) +{ + return withFileNode( "getattr", path, NULL, _do_getattr, stbuf ); +} + +int encfs_fgetattr(const char *path, struct stat *stbuf, + struct fuse_file_info *fi) +{ + return withFileNode( "fgetattr", path, fi, _do_getattr, stbuf ); +} + +int encfs_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler) +{ + EncFS_Context *ctx = context(); + + int res = ESUCCESS; + shared_ptr FSRoot = ctx->getRoot(&res); + if(!FSRoot) + return res; + + try + { + + DirTraverse dt = FSRoot->openDir( path ); + + rLog(Info, "getdir on %s", FSRoot->cipherPath(path).c_str()); + + if(dt.valid()) + { + int fileType = 0; + ino_t inode = 0; + + std::string name = dt.nextPlaintextName( &fileType, &inode ); + while( !name.empty() ) + { + res = filler( h, name.c_str(), fileType, inode ); + + if(res != ESUCCESS) + break; + + name = dt.nextPlaintextName( &fileType, &inode ); + } + } else + { + rInfo("getdir request invalid, path: '%s'", path); + } + + return res; + } catch( rlog::Error &err ) + { + rError("Error caught in getdir"); + err.log( _RLWarningChannel ); + return -EIO; + } +} + +int encfs_mknod(const char *path, mode_t mode, dev_t rdev) +{ + EncFS_Context *ctx = context(); + + int res = -EIO; + shared_ptr FSRoot = ctx->getRoot(&res); + if(!FSRoot) + return res; + + try + { + shared_ptr fnode = FSRoot->lookupNode( path, "mknod" ); + + rLog(Info, "mknod on %s, mode %i, dev %" PRIi64, + fnode->cipherName(), mode, (int64_t)rdev); + + uid_t uid = 0; + gid_t gid = 0; + if(ctx->publicFilesystem) + { + fuse_context *context = fuse_get_context(); + uid = context->uid; + gid = context->gid; + } + res = fnode->mknod( mode, rdev, uid, gid ); + // Is this error due to access problems? + if(ctx->publicFilesystem && -res == EACCES) + { + // try again using the parent dir's group + string parent = fnode->plaintextParent(); + rInfo("trying public filesystem workaround for %s", parent.c_str()); + shared_ptr dnode = + FSRoot->lookupNode( parent.c_str(), "mknod" ); + + struct stat st; + if(dnode->getAttr( &st ) == 0) + res = fnode->mknod( mode, rdev, uid, st.st_gid ); + } + } catch( rlog::Error &err ) + { + rError("error caught in mknod"); + err.log( _RLWarningChannel ); + } + return res; +} + +int encfs_mkdir(const char *path, mode_t mode) +{ + fuse_context *fctx = fuse_get_context(); + EncFS_Context *ctx = context(); + + int res = -EIO; + shared_ptr FSRoot = ctx->getRoot(&res); + if(!FSRoot) + return res; + + try + { + uid_t uid = 0; + gid_t gid = 0; + if(ctx->publicFilesystem) + { + uid = fctx->uid; + gid = fctx->gid; + } + res = FSRoot->mkdir( path, mode, uid, gid ); + // Is this error due to access problems? + if(ctx->publicFilesystem && -res == EACCES) + { + // try again using the parent dir's group + string parent = parentDirectory( path ); + shared_ptr dnode = + FSRoot->lookupNode( parent.c_str(), "mkdir" ); + + struct stat st; + if(dnode->getAttr( &st ) == 0) + res = FSRoot->mkdir( path, mode, uid, st.st_gid ); + } + } catch( rlog::Error &err ) + { + rError("error caught in mkdir"); + err.log( _RLWarningChannel ); + } + return res; +} + +int encfs_unlink(const char *path) +{ + EncFS_Context *ctx = context(); + + int res = -EIO; + shared_ptr FSRoot = ctx->getRoot(&res); + if(!FSRoot) + return res; + + try + { + // let DirNode handle it atomically so that it can handle race + // conditions + res = FSRoot->unlink( path ); + } catch( rlog::Error &err ) + { + rError("error caught in unlink"); + err.log( _RLWarningChannel ); + } + return res; +} + + +int _do_rmdir(EncFS_Context *, const string &cipherPath, int ) +{ + return rmdir( cipherPath.c_str() ); +} + +int encfs_rmdir(const char *path) +{ + return withCipherPath( "rmdir", path, _do_rmdir, 0 ); +} + + +int _do_readlink(EncFS_Context *ctx, const string &cyName, + tuple data ) +{ + char *buf = data.get<0>(); + size_t size = data.get<1>(); + + int res = ESUCCESS; + shared_ptr FSRoot = ctx->getRoot(&res); + if(!FSRoot) + return res; + + res = ::readlink( cyName.c_str(), buf, size-1 ); + + if(res == -1) + return -errno; + + buf[res] = '\0'; // ensure null termination + string decodedName; + try + { + decodedName = FSRoot->plainPath( buf ); + } catch(...) { } + + if(!decodedName.empty()) + { + strncpy(buf, decodedName.c_str(), size-1); + buf[size-1] = '\0'; + + return ESUCCESS; + } else + { + rWarning("Error decoding link"); + return -1; + } +} + +int encfs_readlink(const char *path, char *buf, size_t size) +{ + return withCipherPath( "readlink", path, _do_readlink, + make_tuple(buf, size) ); +} + +int encfs_symlink(const char *from, const char *to) +{ + EncFS_Context *ctx = context(); + + int res = -EIO; + shared_ptr FSRoot = ctx->getRoot(&res); + if(!FSRoot) + return res; + + try + { + // allow fully qualified names in symbolic links. + string fromCName = FSRoot->relativeCipherPath( from ); + string toCName = FSRoot->cipherPath( to ); + + rLog(Info, "symlink %s -> %s", fromCName.c_str(), toCName.c_str()); + + // use setfsuid / setfsgid so that the new link will be owned by the + // uid/gid provided by the fuse_context. + int olduid = -1; + int oldgid = -1; + if(ctx->publicFilesystem) + { + fuse_context *context = fuse_get_context(); + olduid = setfsuid( context->uid ); + oldgid = setfsgid( context->gid ); + } + res = ::symlink( fromCName.c_str(), toCName.c_str() ); + if(olduid >= 0) + setfsuid( olduid ); + if(oldgid >= 0) + setfsgid( oldgid ); + + if(res == -1) + res = -errno; + else + res = ESUCCESS; + } catch( rlog::Error &err ) + { + rError("error caught in symlink"); + err.log( _RLWarningChannel ); + } + return res; +} + +int encfs_link(const char *from, const char *to) +{ + EncFS_Context *ctx = context(); + + int res = -EIO; + shared_ptr FSRoot = ctx->getRoot(&res); + if(!FSRoot) + return res; + + try + { + res = FSRoot->link( from, to ); + } catch( rlog::Error &err ) + { + rError("error caught in link"); + err.log( _RLWarningChannel ); + } + return res; +} + +int encfs_rename(const char *from, const char *to) +{ + EncFS_Context *ctx = context(); + + int res = -EIO; + shared_ptr FSRoot = ctx->getRoot(&res); + if(!FSRoot) + return res; + + try + { + res = FSRoot->rename( from, to ); + } catch( rlog::Error &err ) + { + rError("error caught in rename"); + err.log( _RLWarningChannel ); + } + return res; +} + +int _do_chmod(EncFS_Context *, const string &cipherPath, mode_t mode) +{ + return chmod( cipherPath.c_str(), mode ); +} + +int encfs_chmod(const char *path, mode_t mode) +{ + return withCipherPath( "chmod", path, _do_chmod, mode ); +} + +int _do_chown(EncFS_Context *, const string &cyName, + tuple data) +{ + int res = lchown( cyName.c_str(), data.get<0>(), data.get<1>() ); + return (res == -1) ? -errno : ESUCCESS; +} + +int encfs_chown(const char *path, uid_t uid, gid_t gid) +{ + return withCipherPath( "chown", path, _do_chown, make_tuple(uid, gid)); +} + +int _do_truncate( FileNode *fnode, off_t size ) +{ + return fnode->truncate( size ); +} + +int encfs_truncate(const char *path, off_t size) +{ + return withFileNode( "truncate", path, NULL, _do_truncate, size ); +} + +int encfs_ftruncate(const char *path, off_t size, struct fuse_file_info *fi) +{ + return withFileNode( "ftruncate", path, fi, _do_truncate, size ); +} + +int _do_utime(EncFS_Context *, const string &cyName, struct utimbuf *buf) +{ + int res = utime( cyName.c_str(), buf); + return (res == -1) ? -errno : ESUCCESS; +} + +int encfs_utime(const char *path, struct utimbuf *buf) +{ + return withCipherPath( "utime", path, _do_utime, buf ); +} + +int _do_utimens(EncFS_Context *, const string &cyName, + const struct timespec ts[2]) +{ + struct timeval tv[2]; + tv[0].tv_sec = ts[0].tv_sec; + tv[0].tv_usec = ts[0].tv_nsec / 1000; + tv[1].tv_sec = ts[1].tv_sec; + tv[1].tv_usec = ts[1].tv_nsec / 1000; + + int res = utimes( cyName.c_str(), tv); + return (res == -1) ? -errno : ESUCCESS; +} + +int encfs_utimens(const char *path, const struct timespec ts[2] ) +{ + return withCipherPath( "utimens", path, _do_utimens, ts ); +} + +int encfs_open(const char *path, struct fuse_file_info *file) +{ + EncFS_Context *ctx = context(); + + int res = -EIO; + shared_ptr FSRoot = ctx->getRoot(&res); + if(!FSRoot) + return res; + + try + { + shared_ptr fnode = + FSRoot->openNode( path, "open", file->flags, &res ); + + if(fnode) + { + rLog(Info, "encfs_open for %s, flags %i", fnode->cipherName(), + file->flags); + + if( res >= 0 ) + { + file->fh = (uintptr_t)ctx->putNode(path, fnode); + res = ESUCCESS; + } + } + } catch( rlog::Error &err ) + { + rError("error caught in open"); + err.log( _RLWarningChannel ); + } + + return res; +} + +int _do_flush(FileNode *fnode, int ) +{ + /* Flush can be called multiple times for an open file, so it doesn't + close the file. However it is important to call close() for some + underlying filesystems (like NFS). + */ + int res = fnode->open( O_RDONLY ); + if(res >= 0) + { + int fh = res; + res = close(dup(fh)); + if(res == -1) + res = -errno; + } + + return res; +} + +int encfs_flush(const char *path, struct fuse_file_info *fi) +{ + return withFileNode( "flush", path, fi, _do_flush, 0 ); +} + +/* +Note: This is advisory -- it might benefit us to keep file nodes around for a +bit after they are released just in case they are reopened soon. But that +requires a cache layer. + */ +int encfs_release(const char *path, struct fuse_file_info *finfo) +{ + EncFS_Context *ctx = context(); + + try + { + ctx->eraseNode( path, (void*)(uintptr_t)finfo->fh ); + return ESUCCESS; + } catch( rlog::Error &err ) + { + rError("error caught in release"); + err.log( _RLWarningChannel ); + return -EIO; + } +} + +int _do_read(FileNode *fnode, tuple data) +{ + return fnode->read( data.get<2>(), data.get<0>(), data.get<1>()); +} + +int encfs_read(const char *path, char *buf, size_t size, off_t offset, + struct fuse_file_info *file) +{ + return withFileNode( "read", path, file, _do_read, + make_tuple((unsigned char *)buf, size, offset)); +} + +int _do_fsync(FileNode *fnode, int dataSync) +{ + return fnode->sync( dataSync != 0 ); +} + +int encfs_fsync(const char *path, int dataSync, + struct fuse_file_info *file) +{ + return withFileNode( "fsync", path, file, _do_fsync, dataSync ); +} + +int _do_write(FileNode *fnode, tuple data) +{ + size_t size = data.get<1>(); + if(fnode->write( data.get<2>(), (unsigned char *)data.get<0>(), size )) + return size; + else + return -EIO; +} + +int encfs_write(const char *path, const char *buf, size_t size, + off_t offset, struct fuse_file_info *file) +{ + return withFileNode("write", path, file, _do_write, + make_tuple(buf, size, offset)); +} + +// statfs works even if encfs is detached.. +int encfs_statfs(const char *path, struct statvfs *st) +{ + EncFS_Context *ctx = context(); + + int res = -EIO; + try + { + (void)path; // path should always be '/' for now.. + rAssert( st != NULL ); + string cyName = ctx->rootCipherDir; + + rLog(Info, "doing statfs of %s", cyName.c_str()); + res = statvfs( cyName.c_str(), st ); + if(!res) + { + // adjust maximum name length.. + st->f_namemax = 6 * (st->f_namemax - 2) / 8; // approx.. + } + if(res == -1) + res = -errno; + } catch( rlog::Error &err ) + { + rError("error caught in statfs"); + err.log( _RLWarningChannel ); + } + return res; +} + +#ifdef HAVE_XATTR +int _do_setxattr(EncFS_Context *, const string &cyName, + tuple data) +{ + int res = ::setxattr( cyName.c_str(), data.get<0>(), data.get<1>(), + data.get<2>(), data.get<3>() ); + return (res == -1) ? -errno : res; +} + +int encfs_setxattr( const char *path, const char *name, + const char *value, size_t size, int flags ) +{ + return withCipherPath( "setxattr", path, _do_setxattr, + make_tuple(name, value, size, flags) ); +} + +int _do_getxattr(EncFS_Context *, const string &cyName, + tuple data) +{ + int res = ::getxattr( cyName.c_str(), data.get<0>(), + data.get<1>(), data.get<2>()); + return (res == -1) ? -errno : res; +} + +int encfs_getxattr( const char *path, const char *name, + char *value, size_t size ) +{ + return withCipherPath( "getxattr", path, _do_getxattr, + make_tuple(name, (void *)value, size) ); +} + +int _do_listxattr(EncFS_Context *, const string &cyName, + tuple data) +{ + int res = ::listxattr( cyName.c_str(), data.get<0>(), data.get<1>() ); + return (res == -1) ? -errno : res; +} + +int encfs_listxattr( const char *path, char *list, size_t size ) +{ + return withCipherPath( "listxattr", path, _do_listxattr, + make_tuple(list, size) ); +} + +int _do_removexattr(EncFS_Context *, const string &cyName, const char *name) +{ + int res = ::removexattr( cyName.c_str(), name ); + return (res == -1) ? -errno : res; +} + +int encfs_removexattr( const char *path, const char *name ) +{ + return withCipherPath( "removexattr", path, _do_removexattr, name ); +} +#endif // HAVE_XATTR + +int _do_lock(FileNode *fnode, + tuple data) +{ + int cmd = data.get<0>(); + struct flock *lock = data.get<1>(); + struct fuse_file_info *fi = data.get<2>(); + + int fh = fnode->open( O_RDONLY ); + if(fh >= 0) + { + return ulockmgr_op(fh, cmd, lock, &fi->lock_owner, + sizeof(fi->lock_owner)); + } else + { + rInfo("open failed in lock of %s", fnode->cipherName()); + return fh; + } +} + +#ifdef HAVE_ULOCKMGR_H +int encfs_lock( const char *path, struct fuse_file_info *fi, int cmd, + struct flock *lock) +{ + return withFileNode( "lock", path, fi, + _do_lock, make_tuple(cmd, lock, fi) ); +} +#endif + diff --git a/encfs/encfs.h b/encfs/encfs.h new file mode 100644 index 0000000..3f9e827 --- /dev/null +++ b/encfs/encfs.h @@ -0,0 +1,104 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2003, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + * + */ + +#ifndef _encfs_incl_ +#define _encfs_incl_ + +#include "config.h" +#include +#include + +#if defined(HAVE_SYS_XATTR_H) | defined(HAVE_ATTR_XATTR_H) +#define HAVE_XATTR +#endif + +#ifndef linux +#include + +static __inline int setfsuid(uid_t uid) +{ + uid_t olduid = geteuid(); + + seteuid(uid); + + if (errno != EINVAL) + errno = 0; + + return olduid; +} + +static __inline int setfsgid(gid_t gid) +{ + gid_t oldgid = getegid(); + + setegid(gid); + + if (errno != EINVAL) + errno = 0; + + return oldgid; +} +#endif + +int encfs_getattr(const char *path, struct stat *stbuf); +int encfs_fgetattr(const char *path, struct stat *stbuf, + struct fuse_file_info *fi); +int encfs_readlink(const char *path, char *buf, size_t size); +int encfs_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler); +int encfs_mknod(const char *path, mode_t mode, dev_t rdev); +int encfs_mkdir(const char *path, mode_t mode); +int encfs_unlink(const char *path); +int encfs_rmdir(const char *path); +int encfs_symlink(const char *from, const char *to); +int encfs_rename(const char *from, const char *to); +int encfs_link(const char *from, const char *to); +int encfs_chmod(const char *path, mode_t mode); +int encfs_chown(const char *path, uid_t uid, gid_t gid); +int encfs_truncate(const char *path, off_t size); +int encfs_ftruncate(const char *path, off_t size, + struct fuse_file_info *fi); +int encfs_utime(const char *path, struct utimbuf *buf); +int encfs_open(const char *path, struct fuse_file_info *info); +int encfs_release(const char *path, struct fuse_file_info *info); +int encfs_read(const char *path, char *buf, size_t size, off_t offset, + struct fuse_file_info *info); +int encfs_write(const char *path, const char *buf, size_t size, off_t offset, + struct fuse_file_info *info); +int encfs_statfs(const char *, struct statvfs *fst); +int encfs_flush(const char *, struct fuse_file_info *info); +int encfs_fsync(const char *path, int flags, struct fuse_file_info *info); + +#ifdef HAVE_XATTR +int encfs_setxattr( const char *path, const char *name, const char *value, + size_t size, int flags ); +int encfs_getxattr( const char *path, const char *name, char *value, + size_t size ); +int encfs_listxattr( const char *path, char *list, size_t size ); +int encfs_removexattr( const char *path, const char *name ); +#endif + +#ifdef HAVE_ULOCKMGR_H +struct flock; +int encfs_lock( const char *path, struct fuse_file_info *fi, int cmd, + struct flock *lock ); +#endif + +int encfs_utimens( const char *path, const struct timespec ts[2] ); + +#endif + diff --git a/encfs/encfs.pod b/encfs/encfs.pod new file mode 100644 index 0000000..f3b7c95 --- /dev/null +++ b/encfs/encfs.pod @@ -0,0 +1,464 @@ +=cut +Copyright (c) 2003-2004, Valient Gough +All rights reserved. + +EncFS is free software; you can distribute it and/or modify it under the terms +of the GNU General Public License (GPL), as published by the Free Software +Foundation; either version 2 of the License, or (at your option) any later +version. + +=pod + +=head1 NAME + +encfs - mounts or creates an encrypted virtual filesystem + +=head1 SYNOPSIS + +B [B<--version>] [B<-s>] [B<-f>] [B<-v>|B<--verbose>] +[B<-i MINUTES>|B<--idle=MINUTES>] [B<--extpass=program>] +[B<-S>|B<--stdinpass>] [B<--anykey>] [B<--forcedecode>] +[B<-d>|B<--fuse-debug>] [B<--public>] [B<--no-default-flags>] +I I +[B<--> [I]] + +=head1 DESCRIPTION + +B creates a virtual encrypted filesystem which stores encrypted data in +the I directory and makes the unencrypted data visible at the +I directory. The user must supply a password which is used to +(indirectly) encrypt both filenames and file contents. + +If B is unable to find a supported filesystem at the specified +I, then the user will be asked if they wish to create a new encrypted +filesystem at the specified location. Options will be presented to the user +allowing some control over the algorithms to use. As B matures, there +may be an increasing number of choices. + +=head1 OPTIONS + +=over 4 + +=item B<-i>, B<--idle=MINUTES> + +Enable automatic unmount of the filesystem after a period of inactivity. The +period is specified in minutes, so the shortest timeout period that can be +requested is one minute. B will not automatically unmount if there are +files open within the filesystem, even if they are open in read-only mode. +However simply having files open does not count as activity. + +=item B<-f> + +The B<-f> (I) option causes B to run in the foreground. +Normally B spawns off as a daemon and runs in the background, returning +control to the spawning shell. With the B<-f> option, it will run in the +foreground and any warning/debug log messages will be displayed on standard +error. In the default (background) mode, all log messages are logged via +syslog. + +=item B<-v>, B<--verbose> + +Causes B to enable logging of various debug channels within B. +Normally these logging messages are disabled and have no effect. It is +recommended that you run in foreground (B<-f>) mode when running with verbose +enabled. + +=item B<-s> + +The B<-s> (I) option causes B to run in single threaded +mode. By default, B runs in multi-threaded mode. This option is used +during B development in order to simplify debugging and allow it to run +under memory checking tools.. + +=item B<-d>, B<--fuse-debug> + +Enables debugging within the B library. This should only be used if you +suspect a problem within B itself (not B), as it generates a lot +of low-level data and is not likely to be very helpful in general problem +tracking. Try I mode (B<-v>) first, which gives a higher level view +of what is happening within B. + +=item B<--forcedecode> + +This option only has an effect on filesystems which use MAC block headers. By +default, if a block is decoded and the stored MAC doesn't match what is +calculated, then an IO error is returned to the application and the block is +not returned. However, by specifying B<--forcedecode>, only an error will be +logged and the data will still be returned to the application. This may be +useful for attempting to read corrupted files. + +=item B<--public> + +Attempt to make encfs behave as a typical multi-user filesystem. By default, +all FUSE based filesystems are visible only to the user who mounted them. No +other users (including root) can view the filesystem contents. The B<--public> +option does two things. It adds the FUSE flags "allow_other" and +"default_permission" when mounting the filesystem, which tells FUSE to allow +other users to access the filesystem, and to use the ownership permissions +provided by the filesystem. Secondly, the B<--public> flag changes how encfs's +node creation functions work - as they will try and set ownership of new nodes +based on the caller identification. + +B: In order for this to work, encfs must be run as root -- otherwise +it will not have the ability to change ownership of files. I recommend that +you instead investigate if the fuse allow_other option can be used to do what +you want before considering the use of B<--public>. + +=item B<--> + +The B<--> option tells B to send any remaining arguments directly to +B. In turn, B passes the arguments to B. See +the B help page for information on available commands. + +=item B<--no-default-flags> + +B adds the FUSE flags "use_ino" and "default_permissions" by default, as +of version 1.2.2, because that improves compatibility with some programs.. If +for some reason you need to disable one or both of these flags, use the option +B<--no-default-flags>. + +The following command lines produce the same result: + + encfs raw crypt + encfs --no-default-flags raw crypt -- -o use_ino,default_permissions + +=item B<--extpass=program> + +Specify an external program to use for getting the user password. When the +external program is spawned, the environment variable "RootDir" will be set to +contain the path to the root directory. The program should print the password +to standard output. + +B takes everything returned from the program to be the password, except +for a trailing newline (\n) which will be removed. + +For example, specifying B<--extpass>=I will cause +B to use ssh's password prompt program. + +=item B<-S>, B<--stdinpass> + +Read password from standard input, without prompting. This may be useful for +scripting encfs mounts. + +Note that you should make sure the filesystem and mount points exist first. +Otherwise encfs will prompt for the filesystem creation options, which may +interfere with your script. + +=item B<--anykey> + +Turn off key validation checking. This allows B to be used with +secondary passwords. This could be used to store a separate set of files in an +encrypted filesystem. B ignores files which do not decode properly, so +files created with separate passwords will only be visible when the filesystem +is mounted with their associated password. + +Note that if the primary password is changed (using B), the other +passwords will not be usable unless the primary password is set back to what it +was, as the other passwords rely on an invalid decoding of the volume key, +which will not remain the same if the primary password is changed. + +B: Use this option at your own risk. + +=back + +=head1 EXAMPLES + +Create a new encrypted filesystem. Store the raw (encrypted) data in +"~/.crypt" , and make the unencrypted data visible in "~/crypt". Both +directories are in the home directory in this example. This example shows the +full output of encfs as it asks the user if they wish to create the filesystem: + + % encfs ~/.crypt ~/crypt + Directory "/home/me/.crypt" does not exist, create (y,n)?y + Directory "/home/me/crypt" does not exist, create (y,n)?y + Creating new encrypted volume. + Please choose from one of the following options: + enter "x" for expert configuration mode, + enter "p" for pre-configured paranoia mode, + anything else, or an empty line will select standard mode. + ?> + + Standard configuration selected. + Using cipher Blowfish, key size 160, block size 512 + New Password: + Verify: + +The filesystem is now mounted and visible in I<~/crypt>. If files are created +there, they can be seen in encrypted form in I<~/.crypt>. To unmount the +filesystem, use I with the B<-u> (unmount) option: + + % fusermount -u ~/crypt + +Another example. To mount the same filesystem, but have fusermount name the +mount point '/dev/foo' (as shown in I and other tools which read +/etc/mtab), and also request kernel-level caching of file data (which are both +special arguments to fusermount): + + % encfs ~/.crypt ~/crypt -- -n /dev/foo -c + +Or, if you find strange behavior under some particular program when working in +an encrypted filesystem, it may be helpful to run in verbose mode while +reproducing the problem and send along the output with the problem report: + + % encfs -v -f ~/.crypt ~/crypt 2> encfs-report.txt + +In order to avoid leaking sensitive information through the debugging channels, +all warnings and debug messages (as output in verbose mode) contain only +encrypted filenames. You can use the I program's I function +to decode filenames if desired. + +=head1 CAVEATS + +B is not a true filesystem. It does not deal with any of the actual +storage or maintenance of files. It simply translates requests (encrypting or +decrypting as necessary) and passes the requests through to the underlying +host filesystem. Therefor any limitations of the host filesystem will likely +be inherited by B (or possibly be further limited). + +One such limitation is filename length. If your underlying filesystem limits +you to N characters in a filename, then B will limit you to approximately +3*(N-2)/4. For example if the host filesystem limits to 256 characters, then +B will be limited to 190 character filenames. This is because encrypted +filenames are always longer then plaintext filenames. + +=head1 FILESYSTEM OPTIONS + +When B is given a root directory which does not contain an existing +B filesystem, it will give the option to create one. Note that options +can only be set at filesystem creation time. There is no support for modifying +a filesystem's options in-place. + +If you want to upgrade a filesystem to use newer features, then you need to +create a new filesystem and mount both the old filesystem and new filesystem at +the same time and copy the old to the new. + +Multiple instances of encfs can be run at the same time, including different +versions of encfs, as long as they are compatible with the current FUSE module +on your system. + +A choice is provided for two pre-configured settings ('standard' and +'paranoia'), along with an expert configuration mode. + +I mode uses the following settings: + Cipher: Blowfish + Key Size: 160 bits + Filesystem Block Size: 512 bytes + Filename Encoding: Block encoding with IV chaining + Unique initialization vector file headers + +I mode uses the following settings: + Cipher: AES + Key Size: 256 bits + Filesystem Block Size: 512 bytes + Filename Encoding: Block encoding with IV chaining + Unique initialization vector file headers + Message Authentication Code block headers + External IV Chaining + +In the expert / manual configuration mode, each of the above options is +configurable. Here is a list of current options with some notes about what +they mean: + +=over 4 + +=item I + +Which encryption algorithm to use. The list is generated automatically based +on what supported algorithms B found in the encryption libraries. +When using a recent version of B, Blowfish and AES are the typical +options. + +Blowfish is an 8 byte cipher - encoding 8 bytes at a time. AES is a 16 byte +cipher. + +=item I + +Many, if not all, of the supported ciphers support multiple key lengths. There +is not really much need to have enormous key lengths. Even 160 bits (the +default) is probably overkill. + +=item I + +This is the size (in bytes) that B deals with at one time. Each block +gets its own initialization vector and is encoded in the cipher's +cipher-block-chaining mode. A partial block at the end of a file is encoded +using a stream mode to avoid having to store the filesize somewhere. + +Having larger block sizes reduces the overhead of B a little, but it can +also add overhead if your programs read small parts of files. In order to read +a single byte from a file, the entire block that contains that byte must be +read and decoded, so a large block size adds overhead to small requests. With +write calls it is even worse, as a block must be read and decoded, the change +applied and the block encoded and written back out. + +The default is 512 bytes as of version 1.0. It was hard coded to 64 bytes in +version 0.x, which was not as efficient as the current setting for general +usage. + +=item I + +B. A choice is given between stream encoding of filename and block +encoding. The advantage of stream encoding is that the encoded filenames will +be as short as possible. If you have a filename with a single letter, it will +be very short in the encoded form, where as block encoded filenames are always +rounded up to the block size of the encryption cipher (8 bytes for Blowfish and +16 bytes for AES). + +The advantage of block encoding mode is that filename lenths all come out as a +multiple of the cipher block size. This means that someone looking at your +encrypted data can't tell as much about the length of your filenames. It is +on by default, as it takes a similar amount of time to using the stream cipher. +However stream cipher mode may be useful if you want shorter encrypted +filenames for some reason. + +Prior to version 1.1, only stream encoding was supported. + +=item I + +B. In previous versions of B, each filename element in +a path was encoded separately. So if "foo" encoded to "XXX", then it would +always encode that way (given the same encryption key), no matter if the path +was "a/b/foo", or "aa/foo/cc", etc. That meant it was possible for someone +looking at the encrypted data to see if two files in different directories had +the same name, even though they wouldn't know what that name decoded to. + +With initialization vector chaining, each directory gets its own initialization +vector. So "a/foo" and "b/foo" will have completely different encoded names +for "foo". This features has almost no performance impact (for most +operations), and so is the default in all modes. + +B One significant exception is directory renames. Since the +initialization vector for filename encoding depends on the directory path, any +rename requires re-encoding every filename in the tree of the directory being +changed. If there are thousands of files, then EncFS will have to do thousands +of renames. It may also be possible that EncFS will come across a file that it +can't decode or doesn't have permission to move during the rename operation, in +which case it will attempt to undo any changes it made up to that point and the +rename will fail. + +=item I + +B. In previous versions of B, each file was encoded in the +same way. Each block in a file has always had its own initialization vector, +but in a deterministic way so that block N in one file is encoded in the same +was as block N in another file. That made it possible for someone to tell if +two files were identical (or parts of the file were identical) by comparing the +encoded data. + +With per-file initialization vectors, each file gets its own 64bit random +initialization vector, so that each file is encrypted in a different way. + +This option is enabled by default. + +=item I + +B. This option is closely related to Per-File Initialization +Vectors and Filename Initialization Vector Chaining. Basically it extends the +initialization vector chaining from filenames to the per-file initialization +vector. + +When this option is enabled, the per-file initialization vector is encoded +using the initialization vector derived from the filename initialization vector +chaining code. This means that the data in a file becomes tied to the +filename. If an encrypted file is renamed outside of encfs, it will no longer +be decodable within encfs. Note that unless Block MAC headers are enabled, the +decoding error will not be detected and will result in reading random looking +data. + +There is a cost associated with this. When External IV Chaining is enabled, +hard links will not be allowed within the filesystem, as there would be no way +to properly decode two different filenames pointing to the same data. + +Also, renaming a file requires modifying the file header. So renames will only +be allowed when the user has write access to the file. + +Because of these limits, this option is disabled by default for standard mode +(and enabled by default for paranoia mode). + +=item I + +B. If this is enabled, every block in every file is stored along +with a cryptographic checksum (Message Authentication Code). This makes it +virtually impossible to modify a file without the change being detected by +B. B will refuse to read data which does not pass the checksum, +and will log the error and return an IO error to the application. + +This adds substantial overhead (default being 8 bytes per filesystem block), +plus computational overhead, and is not enabled by default except in paranoia +mode. + +When this is not enabled and if B is asked to read modified or corrupted +data, it will have no way to verify that the decoded data is what was +originally encoded. + +=back + +=head1 Attacks + +The primary goal of B is to protect data off-line. That is, provide a +convenient way of storing files in a way that will frustrate any attempt to +read them if the files are later intercepted. + +Some algorithms in B are also meant to frustrate on-line attacks where +an attacker is assumed to be able to modify the files. + +The most intrusive attacks, where an attacker has complete control of the +user's machine (and can therefor modify B, or B, or the kernel +itself) are not guarded against. Do not assume that encrypted files will +protect your sensitive data if you enter your password into a compromised +computer. How you determine that the computer is safe to use is beyond the +scope of this documentation. + +That said, here are some example attacks and data gathering techniques on the +filesystem contents along with the algorithms B supports to thwart them: + +=over 4 + +=item B: modifying a few bytes of an encrypted file (without knowing +what they will decode to). + +B does not use any form of XOR encryption which would allow +single bytes to be modified without affecting others. Most modifications +would affect dozens or more bytes. Additionally, MAC Block headers can be +used to identify any changes to files. + +=item B: copying a random block of one file to a random block of another file. + +Each block has its own [deterministic] initialization vector. + +=item B: copying block N to block N of another file. + +When the Per-File Initialization Vector support is enabled (default +in 1.1.x filesystems), a copied block will not decode properly when copied to +another file. + +=item B: copying an entire file to another file. + +Can be prevented by enabling External IV Chaining mode. + +=item B: determine if two filenames are the same by looking at encrypted names. + +Filename Initialization Vector chaining prevents this by giving each file a +64-bit initialization vector derived from its full path name. + +=item B: compare if two files contain the same data. + +Per-File Initialization Vector support prevents this. + +=back + +=head1 DISCLAIMER + +This library is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. Please refer to the "COPYING" file distributed with +B for complete details. + +=head1 AUTHORS + +B was written by B<< Valient Gough >>. + +=head1 SEE ALSO + +encfsctl(1) + diff --git a/encfs/encfsctl.cpp b/encfs/encfsctl.cpp new file mode 100644 index 0000000..a00d88e --- /dev/null +++ b/encfs/encfsctl.cpp @@ -0,0 +1,708 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This program is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + + +#include "encfs.h" + +#include "autosprintf.h" +#include "config.h" + +#include "FileUtils.h" +#include "Cipher.h" + +#include "Context.h" + +#include +#include +#include + +#include +#include + +#include +#include +#include +#ifdef __FreeBSD__ +#include +#endif + +#include "i18n.h" + +#ifdef HAVE_SSL +#define NO_DES +#include +#endif + +using namespace rlog; +using namespace std; +using namespace gnu; + + +static int showInfo( int argc, char **argv ); +static int showVersion( int argc, char **argv ); +static int chpasswd( int argc, char **argv ); +static int chpasswdAutomaticly( int argc, char **argv ); +static int cmd_ls( int argc, char **argv ); +static int cmd_decode( int argc, char **argv ); +static int cmd_encode( int argc, char **argv ); +static int cmd_showcruft( int argc, char **argv ); +static int cmd_cat( int argc, char **argv ); +static int cmd_export( int argc, char **argv ); + +struct CommandOpts +{ + const char *name; + int minOptions; + int maxOptions; + int (*func)(int argc, char **argv); + const char *argStr; + const char *usageStr; +} commands[] = +{ + {"info", 1, 1, showInfo, "(root dir)", + // xgroup(usage) + gettext_noop(" -- show information (Default command)")}, + {"passwd", 1, 1, chpasswd, "(root dir)", + // xgroup(usage) + gettext_noop(" -- change password for volume")}, + {"autopasswd", 1, 1, chpasswdAutomaticly, "(root dir)", + // xgroup(usage) + gettext_noop(" -- change password for volume, taking password" + " from standard input.\n\tNo prompts are issued.")}, + {"ls", 1, 2, cmd_ls, 0,0}, + {"showcruft", 1, 1, cmd_showcruft, "(root dir)", + // xgroup(usage) + gettext_noop(" -- show undecodable filenames in the volume")}, + {"cat", 2, 2, cmd_cat, "(root dir) path", + // xgroup(usage) + gettext_noop(" -- decodes the file and cats it to standard out")}, + {"decode", 1, 2, cmd_decode, "(root dir) encoded-name", + // xgroup(usage) + gettext_noop(" -- decodes name and prints plaintext version")}, + {"encode", 1, 2, cmd_encode, "(root dir) [plaintext-name]", + // xgroup(usage) + gettext_noop(" -- encodes a filename and print result")}, + {"export", 2, 2, cmd_export, "(root dir) path", + // xgroup(usage) + gettext_noop(" -- decrypts a volume and writes results to path")}, + {"--version", 0, 0, showVersion, "", + // xgroup(usage) + gettext_noop(" -- print version number and exit")}, + {0,0,0,0,0,0} +}; + + + +static +void usage(const char *name) +{ + cerr << autosprintf(_("encfsctl version %s"), VERSION) << "\n" + << _("Usage:\n") + // displays usage commands, eg "./encfs (root dir) ..." + // xgroup(usage) + << autosprintf(_("%s (root dir)\n" + " -- displays information about the filesystem, or \n"), name); + + int offset = 0; + while(commands[offset].name != 0) + { + if( commands[offset].argStr != 0 ) + { + cerr << "encfsctl " << commands[offset].name << " " + << commands[offset].argStr << "\n" + << gettext( commands[offset].usageStr ) << "\n"; + } + ++offset; + } + + cerr << "\n" + // xgroup(usage) + << autosprintf(_("Example: \n%s info ~/.crypt\n"), name) + << "\n"; +} + +static bool checkDir( string &rootDir ) +{ + if( !isDirectory( rootDir.c_str() )) + { + cerr << autosprintf(_("directory %s does not exist.\n"), + rootDir.c_str()); + return false; + } + if(rootDir[ rootDir.length()-1 ] != '/') + rootDir.append("/"); + + return true; +} + +static int showVersion( int argc, char **argv ) +{ + (void)argc; + (void)argv; + // xgroup(usage) + cerr << autosprintf(_("encfsctl version %s"), VERSION) << "\n"; + + return EXIT_SUCCESS; +} + +static int showInfo( int argc, char **argv ) +{ + (void)argc; + string rootDir = argv[1]; + if( !checkDir( rootDir )) + return EXIT_FAILURE; + + EncFSConfig config; + ConfigType type = readConfig( rootDir, &config ); + + // show information stored in config.. + switch(type) + { + case Config_None: + // xgroup(diag) + cout << _("Unable to load or parse config file\n"); + return EXIT_FAILURE; + case Config_Prehistoric: + // xgroup(diag) + cout << _("A really old EncFS filesystem was found. \n" + "It is not supported in this EncFS build.\n"); + return EXIT_FAILURE; + case Config_V3: + // xgroup(diag) + cout << "\n" << autosprintf(_("Version 3 configuration; " + "created by %s\n"), config.creator.c_str()); + break; + case Config_V4: + // xgroup(diag) + cout << "\n" << autosprintf(_("Version 4 configuration; " + "created by %s\n"), config.creator.c_str()); + break; + case Config_V5: + // xgroup(diag) + cout << "\n" << autosprintf(_("Version 5 configuration; " + "created by %s (revision %i)\n"), config.creator.c_str(), + config.subVersion); + break; + } + + showFSInfo( config ); + + return EXIT_SUCCESS; +} + +static RootPtr initRootInfo(const char* crootDir) +{ + string rootDir(crootDir); + RootPtr result; + + if(checkDir( rootDir )) + { + shared_ptr opts( new EncFS_Opts() ); + opts->rootDir = rootDir; + opts->createIfNotFound = false; + opts->checkKey = false; + result = initFS( NULL, opts ); + } + + if(!result) + cerr << _("Unable to initialize encrypted filesystem - check path.\n"); + + return result; +} + +static int cmd_decode( int argc, char **argv ) +{ + RootPtr rootInfo = initRootInfo(argv[1]); + + if(!rootInfo) + return EXIT_FAILURE; + + if( argc > 2 ) + { + string name = rootInfo->root->plainPath( argv[2] ); + cout << name << "\n"; + } else + { + do + { + string name; + cin >> name; + if(name.empty()) + break; + + name = rootInfo->root->plainPath( name.c_str() ); + cout << name << "\n"; + } while(1); + } + return EXIT_SUCCESS; +} + +static int cmd_encode( int argc, char **argv ) +{ + RootPtr rootInfo = initRootInfo(argv[1]); + + if(!rootInfo) + return EXIT_FAILURE; + + if( argc > 2 ) + { + string name = rootInfo->root->cipherPath( argv[2] ); + cout << name << "\n"; + } else + { + do + { + string name; + cin >> name; + if(name.empty()) + break; + + name = rootInfo->root->cipherPath( name.c_str() ); + cout << name << "\n"; + } while(1); + } + return EXIT_SUCCESS; +} + +static int cmd_ls( int argc, char **argv ) +{ + (void)argc; + + RootPtr rootInfo = initRootInfo(argv[1]); + + if(!rootInfo) + return EXIT_FAILURE; + + // show files in directory + { + DirTraverse dt = rootInfo->root->openDir("/"); + if(dt.valid()) + { + for(string name = dt.nextPlaintextName(); !name.empty(); + name = dt.nextPlaintextName()) + { + shared_ptr fnode = + rootInfo->root->lookupNode( name.c_str(), "encfsctl-ls" ); + struct stat stbuf; + fnode->getAttr( &stbuf ); + + struct tm stm; + localtime_r( &stbuf.st_mtime, &stm ); + stm.tm_year += 1900; + // TODO: when I add "%s" to the end and name.c_str(), I get a + // seg fault from within strlen. Why ??? + printf("%11i %4i-%02i-%02i %02i:%02i:%02i %s\n", + int(stbuf.st_size), + int(stm.tm_year), int(stm.tm_mon), int(stm.tm_mday), + int(stm.tm_hour), int(stm.tm_min), int(stm.tm_sec), + name.c_str()); + } + } + } + + return EXIT_SUCCESS; +} + +// apply an operation to every block in the file +template +int processContents( const shared_ptr &rootInfo, + const char *path, T &op ) +{ + int errCode = 0; + shared_ptr node = rootInfo->root->openNode( path, "encfsctl", + O_RDONLY, &errCode ); + if(!node) + { + cerr << "unable to open " << path << endl; + return errCode; + } else + { + unsigned char buf[512]; + int blocks = (node->getSize() + sizeof(buf)-1) / sizeof(buf); + // read all the data in blocks + for(int i=0; iread(i*sizeof(buf), buf, sizeof(buf)); + int res = op(buf, bytes); + if(res < 0) + return res; + } + } + return 0; +} + +class WriteOutput +{ + int _fd; +public: + WriteOutput(int fd) { _fd = fd; } + ~WriteOutput() { close(_fd); } + + int operator()(const void *buf, int count) + { + return (int)write(_fd, buf, count); + } +}; + +static int cmd_cat( int argc, char **argv ) +{ + (void)argc; + RootPtr rootInfo = initRootInfo(argv[1]); + + if(!rootInfo) + return EXIT_FAILURE; + + const char *path = argv[2]; + WriteOutput output(STDOUT_FILENO); + int errCode = processContents( rootInfo, path, output ); + + return errCode; +} + +static int copyContents(const shared_ptr &rootInfo, + const char* encfsName, const char* targetName) +{ + shared_ptr node = + rootInfo->root->lookupNode( encfsName, "encfsctl" ); + + if(!node) + { + cerr << "unable to open " << encfsName << endl; + return EXIT_FAILURE; + } else + { + struct stat st; + + if(node->getAttr(&st) != 0) + return EXIT_FAILURE; + + if((st.st_mode & S_IFLNK) == S_IFLNK) + { + string d = rootInfo->root->cipherPath(encfsName); + char linkContents[PATH_MAX+2]; + + if(readlink (d.c_str(), linkContents, PATH_MAX + 1) <= 0) + { + cerr << "unable to read link " << encfsName << endl; + return EXIT_FAILURE; + } + symlink(rootInfo->root->plainPath(linkContents).c_str(), + targetName); + } else + { + int outfd = creat(targetName, st.st_mode); + + WriteOutput output(outfd); + processContents( rootInfo, encfsName, output ); + } + } + return EXIT_SUCCESS; +} + +static int traverseDirs(const shared_ptr &rootInfo, + string volumeDir, string destDir) +{ + // Lookup directory node so we can create a destination directory + // with the same permissions + { + struct stat st; + shared_ptr dirNode = + rootInfo->root->lookupNode( volumeDir.c_str(), "encfsctl" ); + if(dirNode->getAttr(&st)) + return EXIT_FAILURE; + + mkdir(destDir.c_str(), st.st_mode); + } + + // show files in directory + DirTraverse dt = rootInfo->root->openDir(volumeDir.c_str()); + if(dt.valid()) + { + for(string name = dt.nextPlaintextName(); !name.empty(); + name = dt.nextPlaintextName()) + { + // Recurse to subdirectories + if(name != "." && name != "..") + { + string plainPath = volumeDir + name; + string cpath = rootInfo->root->cipherPath(plainPath.c_str()); + string destName = destDir + name; + + if(isDirectory(cpath.c_str())) + traverseDirs(rootInfo, (plainPath + '/').c_str(), + destName + '/'); + else + { + int r = copyContents(rootInfo, plainPath.c_str(), + destName.c_str()); + + if(r != EXIT_SUCCESS) + return r; + } + } + } + } + return EXIT_SUCCESS; +} + +static int cmd_export( int argc, char **argv ) +{ + (void)argc; + + RootPtr rootInfo = initRootInfo(argv[1]); + + if(!rootInfo) + return EXIT_FAILURE; + + string destDir = argv[2]; + // if the dir doesn't exist, then create it (with user permission) + if(!checkDir(destDir) && !userAllowMkdir(destDir.c_str(), 0700)) + return EXIT_FAILURE; + + return traverseDirs(rootInfo, "/", destDir); +} + +int showcruft( const shared_ptr &rootInfo, const char *dirName ) +{ + int found = 0; + DirTraverse dt = rootInfo->root->openDir( dirName ); + if(dt.valid()) + { + bool showedDir = false; + for(string name = dt.nextInvalid(); !name.empty(); + name = dt.nextInvalid()) + { + string cpath = rootInfo->root->cipherPath( dirName ); + cpath += '/'; + cpath += name; + + if(!showedDir) + { + // just before showing a list of files in a directory + cout << autosprintf(_("In directory %s: \n"), dirName); + showedDir = true; + } + ++found; + cout << cpath << "\n"; + } + + // now go back and look for directories to recurse into.. + dt = rootInfo->root->openDir( dirName ); + if(dt.valid()) + { + for(string name = dt.nextPlaintextName(); !name.empty(); + name = dt.nextPlaintextName()) + { + if( name == "." || name == "..") + continue; + + string plainPath = dirName; + plainPath += '/'; + plainPath += name; + + string cpath = rootInfo->root->cipherPath( plainPath.c_str() ); + + if(isDirectory( cpath.c_str() )) + found += showcruft( rootInfo, plainPath.c_str() ); + } + } + } + + return found; +} + +/* + iterate recursively through the filesystem and print out names of files + which have filenames which cannot be decoded with the given key.. +*/ +static int cmd_showcruft( int argc, char **argv ) +{ + (void)argc; + + RootPtr rootInfo = initRootInfo(argv[1]); + + if(!rootInfo) + return EXIT_FAILURE; + + int filesFound = showcruft( rootInfo, "/" ); + + cerr << autosprintf( + ngettext("Found %i invalid file.", "Found %i invalid files.", + filesFound), filesFound) << "\n"; + + return EXIT_SUCCESS; +} + +static int do_chpasswd( bool useStdin, int argc, char **argv ) +{ + (void)argc; + string rootDir = argv[1]; + if( !checkDir( rootDir )) + return EXIT_FAILURE; + + EncFSConfig config; + ConfigType cfgType = readConfig( rootDir, &config ); + + if(cfgType == Config_None) + { + cout << _("Unable to load or parse config file\n"); + return EXIT_FAILURE; + } + + // instanciate proper cipher + shared_ptr cipher = Cipher::New( + config.cipherIface, config.keySize ); + if(!cipher) + { + cout << autosprintf(_("Unable to find specified cipher \"%s\"\n"), + config.cipherIface.name().c_str()); + return EXIT_FAILURE; + } + + // ask for existing password + cout << _("Enter current Encfs password\n"); + CipherKey userKey = getUserKey( cipher, useStdin ); + if(!userKey) + return EXIT_FAILURE; + + rAssert( (int)config.keyData.length() == cipher->encodedKeySize() ); + + + // decode volume key using user key -- at this point we detect an incorrect + // password if the key checksum does not match (causing readKey to fail). + CipherKey volumeKey = cipher->readKey( + (unsigned char*)config.keyData.data(), userKey ); + + if(!volumeKey) + { + cout << _("Invalid password\n"); + return EXIT_FAILURE; + } + + // Now, get New user key.. + userKey.reset(); + cout << _("Enter new Encfs password\n"); + if( useStdin ) + userKey = getUserKey( cipher, true ); + else + userKey = getNewUserKey( cipher ); + + // re-encode the volume key using the new user key and write it out.. + int result = EXIT_FAILURE; + if(userKey) + { + int encodedKeySize = cipher->encodedKeySize(); + unsigned char *keyBuf = new unsigned char[ encodedKeySize ]; + + // encode volume key with new user key + cipher->writeKey( volumeKey, keyBuf, userKey ); + userKey.reset(); + + config.keyData.assign( (char*)keyBuf, encodedKeySize ); + delete[] keyBuf; + + if(saveConfig( cfgType, rootDir, &config )) + { + // password modified -- changes volume key of filesystem.. + cout << _("Volume Key successfully updated.\n"); + result = EXIT_SUCCESS; + } else + { + cout << _("Error saving modified config file.\n"); + } + } + + volumeKey.reset(); + + return result; +} + +static int chpasswd( int argc, char **argv ) +{ + return do_chpasswd( false, argc, argv ); +} + +static int chpasswdAutomaticly( int argc, char **argv ) +{ + return do_chpasswd( true, argc, argv ); +} + + +int main(int argc, char **argv) +{ + RLogInit( argc, argv ); + +#ifdef LOCALEDIR + setlocale( LC_ALL, "" ); + bindtextdomain( PACKAGE, LOCALEDIR ); + textdomain( PACKAGE ); +#endif + +#ifdef HAVE_SSL + SSL_load_error_strings(); + SSL_library_init(); +#endif + + StdioNode *slog = new StdioNode( STDERR_FILENO ); + slog->subscribeTo( GetGlobalChannel("error") ); + slog->subscribeTo( GetGlobalChannel("warning") ); +#ifndef NO_DEBUG + //slog->subscribeTo( GetGlobalChannel("debug") ); +#endif + + if(argc < 2) + { + usage( argv[0] ); + return EXIT_FAILURE; + } + + if(argc == 2 && !(*argv[1] == '-' && *(argv[1]+1) == '-')) + { + // default command when only 1 argument given -- treat the argument as + // a directory.. + return showInfo( argc, argv ); + } else + { + // find the specified command + int offset = 0; + while(commands[offset].name != 0) + { + if(!strcmp( argv[1], commands[offset].name )) + break; + ++offset; + } + + if(commands[offset].name == 0) + { + cerr << autosprintf(_("invalid command: \"%s\""), argv[1]) << endl; + } else + { + if((argc-2 < commands[offset].minOptions) || + (argc-2 > commands[offset].maxOptions)) + { + cerr << autosprintf( + _("Incorrect number of arguments for command \"%s\""), + argv[1]) << endl; + } else + return (*commands[offset].func)( argc-1, argv+1 ); + } + } + + return EXIT_FAILURE; +} + + diff --git a/encfs/encfsctl.pod b/encfs/encfsctl.pod new file mode 100644 index 0000000..e58b99a --- /dev/null +++ b/encfs/encfsctl.pod @@ -0,0 +1,99 @@ +=cut +Copyright (c) 2003-2004, Valient Gough +All rights reserved. + +EncFS is free software; you can distribute it and/or modify it under the terms +of the GNU General Public License (GPL), as published by the Free Software +Foundation; either version 2 of the License, or (at your option) any later +version. + +=pod + +=head1 NAME + +encfsctl - administrative tool for working with EncFS filesystems + +=head1 SYNOPSIS + +B [I I] + +B I + +B info I + +B passwd I + +B showcruft I + +B decode I [encoded name] + +=head1 DESCRIPTION + +B is an administrative tool for working with EncFS filesystems. It +is capable of changing the user supplied password, displaying basic information +about an encrypted volume, and other related operations. + +=head1 COMMANDS + +=over 4 + +=item B + +Display basic information about the filesystem. Takes a single argument, +I, which is the root directory of the encrypted filesystem. The +filesystem need not be mounted. B is also the default command if only a +root directory is provided on the command line. + +=item B + +Allows changing the password of the encrypted filesystem. The user will be +prompted for the existing password and the new password. + +=item B + +Recursively search through the entire volume and display all files which are +not decodable (only checks filename encoding, not block MAC headers). This +might be useful for cleanup in case you've made use of features which create +files which are not decodable under the primary key. + +=item B + +Allows you to specify an encoded name on the command line, and displayed a +decoded version. This is mostly useful for debugging, as debug messages always +display encrypted filenames (to avoid leaking sensitive data through the debug +channels). So this command provides a way to decode the filenames. + +If no name is specified on the command line, then a list of filenames will be +read from stdin and decoded. + +=back + +=head1 EXAMPLES + +Show information about an encrypted filesystem: + + % encfsctl info ~/.crypt + + Version 5 configuration; created by EncFS 1.1 (revision 20040504) + Filesystem cipher: "ssl/aes" , version 2:1:1 + Filename encoding: "nameio/block" , version 3:0:1 + Key Size: 192 bits + Block Size: 512 bytes + Each file contains 8 byte header with unique IV data. + Filesname encoded using IV chaining mode. + +=head1 DISCLAIMER + +This library is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. Please refer to the "COPYING" file distributed with +B for complete details. + +=head1 AUTHORS + +EncFS was written by Valient Gough . + +=head1 SEE ALSO + +encfs(1) + diff --git a/encfs/encfssh b/encfs/encfssh new file mode 100644 index 0000000..04603c9 --- /dev/null +++ b/encfs/encfssh @@ -0,0 +1,67 @@ +#!/bin/sh + +# This script mounts an encfs filesystem, starts a shell in the mounted +# directory, and then unmounts the filesystem when the shell exits. +# This is an equivalent of the cfssh utility for cfs. +# Contributed by David Rosenstrauch. + +canonicalize() { + cd "$1" + pwd +} + + +if [ -z "$1" -o "$1" = "-h" ]; then + echo Usage: encfssh encrypted_directory [unencrypted-directory [-p]] + echo " -p mount the unencrypted directory as public" + exit 1 +fi + +enc_dir=$1 +unenc_dir_given=false +mount_public=false +if [ ! -z "$2" ]; then + unenc_dir_given=true + unenc_dir=$2 + for arg in "$@" ; do + if [ "$arg" = "-p" ]; then + mount_public=true + fi + done +else + unenc_dir=.$RANDOM.$RANDOM +fi + +if [ ! -d "$enc_dir" ]; then + mkdir $enc_dir +fi +if [ ! -d "$unenc_dir" ]; then + mkdir $unenc_dir +fi + +enc_dir=$(canonicalize "$enc_dir") +unenc_dir=$(canonicalize "$unenc_dir") + +options="" +if $unenc_dir_given; then + if $mount_public; then + options="-- -o allow_other" + fi +fi + +# Attach the directory and change into it +if encfs $enc_dir $unenc_dir $options; then :; else + echo "encfs failed" + rmdir $unenc_dir + exit 1 +fi +if ! $unenc_dir_given; then + chmod 700 $unenc_dir +fi +echo "Directory is $unenc_dir" +orig_dir=$(pwd) +cd $unenc_dir + +# Set the shell up +exec /bin/sh -c "$SHELL ; cd $orig_dir ; fusermount -u $unenc_dir ; if ! $unenc_dir_given; then rmdir $unenc_dir; fi" + diff --git a/encfs/i18n.h b/encfs/i18n.h new file mode 100644 index 0000000..604d538 --- /dev/null +++ b/encfs/i18n.h @@ -0,0 +1,41 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + */ + +#ifndef _i18n_incl_ +#define _i18n_incl_ + +#if defined(LOCALEDIR) + +# include "gettext.h" +// make shortcut for gettext +# define _(STR) gettext (STR) + +# include "autosprintf.h" +using gnu::autosprintf; + +#else + +# define gettext(STR) (STR) +# define gettext_noop(STR) (STR) +# define _(STR) (STR) +# define N_(STR) (STR) + +#endif + +#endif + + diff --git a/encfs/main.cpp b/encfs/main.cpp new file mode 100644 index 0000000..e146ab6 --- /dev/null +++ b/encfs/main.cpp @@ -0,0 +1,745 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2003-2004, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + * + */ + +#include "encfs.h" +#include "config.h" +#include "autosprintf.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "ConfigReader.h" +#include "Interface.h" +#include "MemoryPool.h" +#include "FileUtils.h" +#include "DirNode.h" +#include "Context.h" + +#include "openssl.h" + +// Fuse version >= 26 requires another argument to fuse_unmount, which we +// don't have. So use the backward compatible call instead.. +extern "C" void fuse_unmount_compat22(const char *mountpoint); +# define fuse_unmount fuse_unmount_compat22 + +#include + +#include "i18n.h" + +#ifndef MAX +inline static int MAX(int a, int b) +{ + return (a > b) ? a : b; +} +#endif + +using namespace std; +using namespace rlog; +using namespace rel; +using namespace gnu; +using boost::shared_ptr; +using boost::scoped_ptr; + +// Maximum number of arguments that we're going to pass on to fuse. Doesn't +// affect how many arguments we can handle, just how many we can pass on.. +const int MaxFuseArgs = 32; +struct EncFS_Args +{ + string mountPoint; // where to make filesystem visible + bool isDaemon; // true == spawn in background, log to syslog + bool isThreaded; // true == threaded + bool isVerbose; // false == only enable warning/error messages + int idleTimeout; // 0 == idle time in minutes to trigger unmount + const char *fuseArgv[MaxFuseArgs]; + int fuseArgc; + + shared_ptr opts; + + // for debugging + // In case someone sends me a log dump, I want to know how what options are + // in effect. Not internationalized, since it is something that is mostly + // useful for me! + string toString() + { + ostringstream ss; + ss << (isDaemon ? "(daemon) " : "(fg) "); + ss << (isThreaded ? "(threaded) " : "(UP) "); + if(idleTimeout > 0) + ss << "(timeout " << idleTimeout << ") "; + if(opts->checkKey) ss << "(keyCheck) "; + if(opts->forceDecode) ss << "(forceDecode) "; + if(opts->ownerCreate) ss << "(ownerCreate) "; + if(opts->useStdin) ss << "(useStdin) "; + if(opts->reverseEncryption) ss << "(reverseEncryption) "; + if(opts->mountOnDemand) ss << "(mountOnDemand) "; + for(int i=0; i(argv), (fuse_operations*)NULL, NULL); +} + +#define PUSHARG(ARG) \ +rAssert(out->fuseArgc < MaxFuseArgs); \ +out->fuseArgv[out->fuseArgc++] = ARG + +static +string slashTerminate( const string &src ) +{ + string result = src; + if( result[ result.length()-1 ] != '/' ) + result.append( "/" ); + return result; +} + +static +bool processArgs(int argc, char *argv[], const shared_ptr &out) +{ + // set defaults + out->isDaemon = true; + out->isThreaded = true; + out->isVerbose = false; + out->idleTimeout = 0; + out->fuseArgc = 0; + out->opts->idleTracking = false; + out->opts->checkKey = true; + out->opts->forceDecode = false; + out->opts->ownerCreate = false; + out->opts->useStdin = false; + out->opts->reverseEncryption = false; + + bool useDefaultFlags = true; + + // pass executable name through + out->fuseArgv[0] = lastPathElement(argv[0]); + ++out->fuseArgc; + + // leave a space for mount point, as FUSE expects the mount point before + // any flags + out->fuseArgv[1] = NULL; + ++out->fuseArgc; + + // TODO: can flags be internationalized? + static struct option long_options[] = { + {"fuse-debug", 0, 0, 'd'}, // Fuse debug mode + {"forcedecode", 0, 0, 'D'}, // force decode + // {"foreground", 0, 0, 'f'}, // foreground mode (no daemon) + {"fuse-help", 0, 0, 'H'}, // fuse_mount usage + {"idle", 1, 0, 'i'}, // idle timeout + {"anykey", 0, 0, 'k'}, // skip key checks + {"no-default-flags", 0, 0, 'N'}, // don't use default fuse flags + {"ondemand", 0, 0, 'm'}, // mount on-demand + {"public", 0, 0, 'P'}, // public mode + {"extpass", 1, 0, 'p'}, // external password program + // {"single-thread", 0, 0, 's'}, // single-threaded mode + {"stdinpass", 1, 0, 'S'}, // read password from stdin + {"verbose", 0, 0, 'v'}, // verbose mode + {"version", 0, 0, 'V'}, //version + {"reverse", 0, 0, 'r'}, // reverse encryption + {0,0,0,0} + }; + + while (1) + { + int option_index = 0; + + // 's' : single-threaded mode + // 'f' : foreground mode + // 'v' : verbose mode (same as --verbose) + // 'd' : fuse debug mode (same as --fusedebug) + // 'i' : idle-timeout, takes argument + // 'm' : mount-on-demand + // 'S' : password from stdin + // 'o' : arguments meant for fuse + int res = getopt_long( argc, argv, "HsSfvdmi:o:", + long_options, &option_index); + + if(res == -1) + break; + + switch( res ) + { + case 's': + out->isThreaded = false; + break; + case 'S': + out->opts->useStdin = true; + break; + case 'f': + out->isDaemon = false; + // this option was added in fuse 2.x + PUSHARG("-f"); + break; + case 'v': + out->isVerbose = true; + break; + case 'd': + PUSHARG("-d"); + break; + case 'i': + out->idleTimeout = strtol( optarg, (char**)NULL, 10); + out->opts->idleTracking = true; + break; + case 'k': + out->opts->checkKey = false; + break; + case 'D': + out->opts->forceDecode = true; + break; + case 'r': + out->opts->reverseEncryption = true; + break; + case 'm': + out->opts->mountOnDemand = true; + break; + case 'N': + useDefaultFlags = false; + break; + case 'o': + PUSHARG("-o"); + PUSHARG( optarg ); + break; + case 'p': + out->opts->passwordProgram.assign( optarg ); + break; + case 'P': + if(geteuid() != 0) + rWarning(_("option '--public' ignored for non-root user")); + else + { + out->opts->ownerCreate = true; + // add 'allow_other' option + // add 'default_permissions' option (default) + PUSHARG("-o"); + PUSHARG("allow_other"); + } + break; + case 'V': + // xgroup(usage) + cerr << autosprintf(_("encfs version %s"), VERSION) << endl; + exit(EXIT_SUCCESS); + break; + case 'H': + FuseUsage(); + exit(EXIT_SUCCESS); + break; + case '?': + // invalid options.. + break; + case ':': + // missing parameter for option.. + break; + default: + rWarning(_("getopt error: %i"), res); + break; + } + } + + if(!out->isThreaded) + PUSHARG("-s"); + + if(useDefaultFlags) + { + PUSHARG("-o"); + PUSHARG("use_ino"); + PUSHARG("-o"); + PUSHARG("default_permissions"); + } + + // we should have at least 2 arguments left over - the source directory and + // the mount point. + if(optind+2 <= argc) + { + out->opts->rootDir = slashTerminate( argv[optind++] ); + out->mountPoint = argv[optind++]; + } else + { + // no mount point specified + rWarning(_("Missing one or more arguments, aborting.")); + return false; + } + + // If there are still extra unparsed arguments, pass them onto FUSE.. + if(optind < argc) + { + rAssert(out->fuseArgc < MaxFuseArgs); + + while(optind < argc) + { + rAssert(out->fuseArgc < MaxFuseArgs); + out->fuseArgv[out->fuseArgc++] = argv[optind]; + ++optind; + } + } + + // sanity check + if(out->isDaemon && + (!isAbsolutePath( out->mountPoint.c_str() ) || + !isAbsolutePath( out->opts->rootDir.c_str() ) ) + ) + { + cerr << + // xgroup(usage) + _("When specifying daemon mode, you must use absolute paths " + "(beginning with '/')") + << endl; + return false; + } + + // the raw directory may not be a subdirectory of the mount point. + { + string testMountPoint = slashTerminate( out->mountPoint ); + string testRootDir = + out->opts->rootDir.substr(0, testMountPoint.length()); + + if( testMountPoint == testRootDir ) + { + cerr << + // xgroup(usage) + _("The raw directory may not be a subdirectory of the " + "mount point.") << endl; + return false; + } + } + + if(out->opts->mountOnDemand && out->opts->passwordProgram.empty()) + { + cerr << + // xgroup(usage) + _("Must set password program when using mount-on-demand") + << endl; + return false; + } + + // check that the directories exist, or that we can create them.. + if(!isDirectory( out->opts->rootDir.c_str() ) && + !userAllowMkdir( out->opts->rootDir.c_str() ,0700)) + { + rWarning(_("Unable to locate root directory, aborting.")); + return false; + } + if(!isDirectory( out->mountPoint.c_str() ) && + !userAllowMkdir( out->mountPoint.c_str(),0700)) + { + rWarning(_("Unable to locate mount point, aborting.")); + return false; + } + + // fill in mount path for fuse + out->fuseArgv[1] = out->mountPoint.c_str(); + + return true; +} + +static void * idleMonitor(void *); + +void *encfs_init(fuse_conn_info *conn) +{ + EncFS_Context *ctx = (EncFS_Context*)fuse_get_context()->private_data; + + // set fuse connection options + conn->async_read = true; + + // if an idle timeout is specified, then setup a thread to monitor the + // filesystem. + if(ctx->args->idleTimeout > 0) + { + rDebug("starting idle monitoring thread"); + ctx->running = true; + + int res = pthread_create( &ctx->monitorThread, 0, idleMonitor, + (void*)ctx ); + if(res != 0) + { + rError("error starting idle monitor thread, " + "res = %i, errno = %i", res, errno); + } + } + + if(ctx->args->isDaemon && oldStderr >= 0) + { + rInfo("Closing stderr"); + close(oldStderr); + oldStderr = -1; + } + + return (void*)ctx; +} + +void encfs_destroy( void *_ctx ) +{ + EncFS_Context *ctx = (EncFS_Context*)_ctx; + if(ctx->args->idleTimeout > 0) + { + ctx->running = false; + + // wake up the thread if it is waiting.. + rDebug("waking up monitoring thread"); + pthread_mutex_lock( &ctx->wakeupMutex ); + pthread_cond_signal( &ctx->wakeupCond ); + pthread_mutex_unlock( &ctx->wakeupMutex ); + rDebug("joining with idle monitoring thread"); + pthread_join( ctx->monitorThread , 0 ); + rDebug("join done"); + } +} + +int main(int argc, char *argv[]) +{ + // initialize the logging library + RLogInit( argc, argv ); + +#ifdef LOCALEDIR + setlocale( LC_ALL, "" ); + bindtextdomain( PACKAGE, LOCALEDIR ); + textdomain( PACKAGE ); +#endif + + // log to stderr by default.. + scoped_ptr slog( new StdioNode( STDERR_FILENO ) ); + scoped_ptr logNode; + + // show error and warning output + slog->subscribeTo( GetGlobalChannel("error") ); + slog->subscribeTo( GetGlobalChannel("warning") ); + + // anything that comes from the user should be considered tainted until + // we've processed it and only allowed through what we support. + shared_ptr encfsArgs( new EncFS_Args ); + for(int i=0; ifuseArgv[i] = NULL; // libfuse expects null args.. + + if(argc == 1 || !processArgs(argc, argv, encfsArgs)) + { + usage(argv[0]); + return EXIT_FAILURE; + } + + if(encfsArgs->isVerbose) + { + // subscribe to more logging channels.. + slog->subscribeTo( GetGlobalChannel("info") ); + slog->subscribeTo( GetGlobalChannel("debug") ); + } + + rDebug("Root directory: %s", encfsArgs->opts->rootDir.c_str()); + rDebug("Fuse arguments: %s", encfsArgs->toString().c_str()); + + fuse_operations encfs_oper; + // in case this code is compiled against a newer FUSE library and new + // members have been added to fuse_operations, make sure they get set to + // 0.. + memset(&encfs_oper, 0, sizeof(fuse_operations)); + + encfs_oper.getattr = encfs_getattr; + encfs_oper.readlink = encfs_readlink; + encfs_oper.getdir = encfs_getdir; + encfs_oper.mknod = encfs_mknod; + encfs_oper.mkdir = encfs_mkdir; + encfs_oper.unlink = encfs_unlink; + encfs_oper.rmdir = encfs_rmdir; + encfs_oper.symlink = encfs_symlink; + encfs_oper.rename = encfs_rename; + encfs_oper.link = encfs_link; + encfs_oper.chmod = encfs_chmod; + encfs_oper.chown = encfs_chown; + encfs_oper.truncate = encfs_truncate; + encfs_oper.utime = encfs_utime; + encfs_oper.open = encfs_open; + encfs_oper.read = encfs_read; + encfs_oper.write = encfs_write; + encfs_oper.statfs = encfs_statfs; + encfs_oper.flush = encfs_flush; + encfs_oper.release = encfs_release; + encfs_oper.fsync = encfs_fsync; +#ifdef HAVE_XATTR + encfs_oper.setxattr = encfs_setxattr; + encfs_oper.getxattr = encfs_getxattr; + encfs_oper.listxattr = encfs_listxattr; + encfs_oper.removexattr = encfs_removexattr; +#endif // HAVE_XATTR + //encfs_oper.opendir = encfs_opendir; + //encfs_oper.readdir = encfs_readdir; + //encfs_oper.releasedir = encfs_releasedir; + //encfs_oper.fsyncdir = encfs_fsyncdir; + encfs_oper.init = encfs_init; + encfs_oper.destroy = encfs_destroy; + //encfs_oper.access = encfs_access; + //encfs_oper.create = encfs_create; + encfs_oper.ftruncate = encfs_ftruncate; + encfs_oper.fgetattr = encfs_fgetattr; +#ifdef HAVE_ULOCKMGR_H + encfs_oper.lock = encfs_lock; +#endif // HAVE_ULOCKMGR_H + encfs_oper.utimens = encfs_utimens; + //encfs_oper.bmap = encfs_bmap; + + openssl_init( encfsArgs->isThreaded ); + + // context is not a smart pointer because it will live for the life of + // the filesystem. + EncFS_Context *ctx = new EncFS_Context; + ctx->publicFilesystem = encfsArgs->opts->ownerCreate; + RootPtr rootInfo = initFS( ctx, encfsArgs->opts ); + + int returnCode = EXIT_FAILURE; + + if( rootInfo ) + { + // set the globally visible root directory node + ctx->setRoot( rootInfo->root ); + ctx->args = encfsArgs; + ctx->opts = encfsArgs->opts; + + if(encfsArgs->isThreaded == false && encfsArgs->idleTimeout > 0) + { + // xgroup(usage) + cerr << _("Note: requested single-threaded mode, but an idle\n" + "timeout was specified. The filesystem will operate\n" + "single-threaded, but threads will still be used to\n" + "implement idle checking.") << endl; + } + + // reset umask now, since we don't want it to interfere with the + // pass-thru calls.. + umask( 0 ); + + if(encfsArgs->isDaemon) + { + // switch to logging just warning and error messages via syslog + logNode.reset( new SyslogNode( "encfs" ) ); + logNode->subscribeTo( GetGlobalChannel("warning") ); + logNode->subscribeTo( GetGlobalChannel("error") ); + + // disable stderr reporting.. + slog.reset(); + + // keep around a pointer just in case we end up needing it to + // report a fatal condition later (fuse_main exits unexpectedly)... + oldStderr = dup( STDERR_FILENO ); + } + + try + { + time_t startTime, endTime; + + // FIXME: workaround for fuse_main returning an error on normal + // exit. Only print information if fuse_main returned + // immediately.. + time( &startTime ); + + // fuse_main returns an error code in newer versions of fuse.. + int res = fuse_main( encfsArgs->fuseArgc, + const_cast(encfsArgs->fuseArgv), + &encfs_oper, (void*)ctx); + + time( &endTime ); + + if(res == 0) + returnCode = EXIT_SUCCESS; + + if(res != 0 && encfsArgs->isDaemon && (oldStderr >= 0) + && (endTime - startTime <= 1) ) + { + // the users will not have seen any message from fuse, so say a + // few words in libfuse's memory.. + FILE *out = fdopen( oldStderr, "a" ); + // xgroup(usage) + fprintf(out, _("fuse failed. Common problems:\n" + " - fuse kernel module not installed (modprobe fuse)\n" + " - invalid options -- see usage message\n")); + fclose(out); + } + } catch(std::exception &ex) + { + rError(_("Internal error: Caught exception from main loop: %s"), + ex.what()); + } catch(...) + { + rError(_("Internal error: Caught unexpected exception")); + } + } + + // cleanup so that we can check for leaked resources.. + rootInfo.reset(); + ctx->setRoot( shared_ptr() ); + + MemoryPool::destroyAll(); + openssl_shutdown( encfsArgs->isThreaded ); + + return returnCode; +} + +/* + Idle monitoring thread. This is only used when idle monitoring is enabled. + It will cause the filesystem to be automatically unmounted (causing us to + commit suicide) if the filesystem stays idle too long. Idle time is only + checked if there are no open files, as I don't want to risk problems by + having the filesystem unmounted from underneath open files! +*/ +const int ActivityCheckInterval = 10; +static bool unmountFS(EncFS_Context *ctx); + +static +void * idleMonitor(void *_arg) +{ + EncFS_Context *ctx = (EncFS_Context*)_arg; + shared_ptr arg = ctx->args; + + const int timeoutCycles = 60 * arg->idleTimeout / ActivityCheckInterval; + int idleCycles = 0; + + pthread_mutex_lock( &ctx->wakeupMutex ); + + while(ctx->running) + { + int usage = ctx->getAndResetUsageCounter(); + + if(usage == 0 && ctx->isMounted()) + ++idleCycles; + else + idleCycles = 0; + + if(idleCycles >= timeoutCycles) + { + int openCount = ctx->openFileCount(); + if( openCount == 0 && unmountFS( ctx ) ) + { + // wait for main thread to wake us up + pthread_cond_wait( &ctx->wakeupCond, &ctx->wakeupMutex ); + break; + } + + rDebug("num open files: %i", openCount ); + } + + rDebug("idle cycle count: %i, timeout after %i", idleCycles, + timeoutCycles); + + struct timeval currentTime; + gettimeofday( ¤tTime, 0 ); + struct timespec wakeupTime; + wakeupTime.tv_sec = currentTime.tv_sec + ActivityCheckInterval; + wakeupTime.tv_nsec = currentTime.tv_usec * 1000; + pthread_cond_timedwait( &ctx->wakeupCond, + &ctx->wakeupMutex, &wakeupTime ); + } + + pthread_mutex_unlock( &ctx->wakeupMutex ); + + rDebug("Idle monitoring thread exiting"); + + return 0; +} + +static bool unmountFS(EncFS_Context *ctx) +{ + shared_ptr arg = ctx->args; + if( arg->opts->mountOnDemand ) + { + rDebug("Detaching filesystem %s due to inactivity", + arg->mountPoint.c_str()); + + ctx->setRoot( shared_ptr() ); + return false; + } else + { + // Time to unmount! + // xgroup(diag) + rWarning(_("Unmounting filesystem %s due to inactivity"), + arg->mountPoint.c_str()); + fuse_unmount( arg->mountPoint.c_str() ); + return true; + } +} + diff --git a/encfs/openssl.cpp b/encfs/openssl.cpp new file mode 100644 index 0000000..27a4ffe --- /dev/null +++ b/encfs/openssl.cpp @@ -0,0 +1,109 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2007, Valient Gough + * + * This program is free software: you can 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 . + */ + +#include "openssl.h" + +#include + +#include + +#define NO_DES +#include +#include +#ifndef OPENSSL_NO_ENGINE +#include +#endif + +unsigned long pthreads_thread_id() +{ + return (unsigned long)pthread_self(); +} + +static pthread_mutex_t *crypto_locks = NULL; +void pthreads_locking_callback( int mode, int n, + const char *caller_file, int caller_line ) +{ + (void)caller_file; + (void)caller_line; + + if(!crypto_locks) + { + rDebug("Allocating %i locks for OpenSSL", CRYPTO_num_locks() ); + crypto_locks = new pthread_mutex_t[ CRYPTO_num_locks() ]; + for(int i=0; i + * + ***************************************************************************** + * Copyright (c) 2007, Valient Gough + * + * This program is free software: you can 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 . + */ + +#ifndef _openssl_incl_ +#define _openssl_incl_ + +void openssl_init(bool isThreaded); +void openssl_shutdown(bool isThreaded); + +#endif + + diff --git a/encfs/readpassphrase.cpp b/encfs/readpassphrase.cpp new file mode 100644 index 0000000..b7e4a33 --- /dev/null +++ b/encfs/readpassphrase.cpp @@ -0,0 +1,195 @@ +/* $OpenBSD: readpassphrase.c,v 1.12 2001/12/15 05:41:00 millert Exp $ */ + +/* + * Copyright (c) 2000 Todd C. Miller + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char rcsid[] = "$OpenBSD: readpassphrase.c,v 1.12 2001/12/15 05:41:00 millert Exp $"; +#endif /* LIBC_SCCS and not lint */ + +//#include "includes.h" + +#ifndef HAVE_READPASSPHRASE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef TCSASOFT +# define _T_FLUSH (TCSAFLUSH|TCSASOFT) +#else +# define _T_FLUSH (TCSAFLUSH) +#endif + +/* SunOS 4.x which lacks _POSIX_VDISABLE, but has VDISABLE */ +#if !defined(_POSIX_VDISABLE) && defined(VDISABLE) +# define _POSIX_VDISABLE VDISABLE +#endif + +static volatile sig_atomic_t signo; + +static void handler(int); + +char * +readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags) +{ + ssize_t nr; + int input, output, save_errno; + char ch, *p, *end; + struct termios term, oterm; + struct sigaction sa, saveint, savehup, savequit, saveterm; + struct sigaction savetstp, savettin, savettou; + + /* I suppose we could alloc on demand in this case (XXX). */ + if (bufsiz == 0) { + errno = EINVAL; + return(NULL); + } + +restart: + /* + * Read and write to /dev/tty if available. If not, read from + * stdin and write to stderr unless a tty is required. + */ + if ((input = output = open(_PATH_TTY, O_RDWR)) == -1) { + if (flags & RPP_REQUIRE_TTY) { + errno = ENOTTY; + return(NULL); + } + input = STDIN_FILENO; + output = STDERR_FILENO; + } + + /* + * Catch signals that would otherwise cause the user to end + * up with echo turned off in the shell. Don't worry about + * things like SIGALRM and SIGPIPE for now. + */ + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; /* don't restart system calls */ + sa.sa_handler = handler; + (void)sigaction(SIGINT, &sa, &saveint); + (void)sigaction(SIGHUP, &sa, &savehup); + (void)sigaction(SIGQUIT, &sa, &savequit); + (void)sigaction(SIGTERM, &sa, &saveterm); + (void)sigaction(SIGTSTP, &sa, &savetstp); + (void)sigaction(SIGTTIN, &sa, &savettin); + (void)sigaction(SIGTTOU, &sa, &savettou); + + /* Turn off echo if possible. */ + if (tcgetattr(input, &oterm) == 0) { + memcpy(&term, &oterm, sizeof(term)); + if (!(flags & RPP_ECHO_ON)) + term.c_lflag &= ~(ECHO | ECHONL); +#ifdef VSTATUS + if (term.c_cc[VSTATUS] != _POSIX_VDISABLE) + term.c_cc[VSTATUS] = _POSIX_VDISABLE; +#endif + (void)tcsetattr(input, _T_FLUSH, &term); + } else { + memset(&term, 0, sizeof(term)); + memset(&oterm, 0, sizeof(oterm)); + } + + (void)write(output, prompt, strlen(prompt)); + end = buf + bufsiz - 1; + for (p = buf; (nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r';) { + if (p < end) { + if ((flags & RPP_SEVENBIT)) + ch &= 0x7f; + if (isalpha(ch)) { + if ((flags & RPP_FORCELOWER)) + ch = tolower(ch); + if ((flags & RPP_FORCEUPPER)) + ch = toupper(ch); + } + *p++ = ch; + } + } + *p = '\0'; + save_errno = errno; + if (!(term.c_lflag & ECHO)) + (void)write(output, "\n", 1); + + /* Restore old terminal settings and signals. */ + if (memcmp(&term, &oterm, sizeof(term)) != 0) + (void)tcsetattr(input, _T_FLUSH, &oterm); + (void)sigaction(SIGINT, &saveint, NULL); + (void)sigaction(SIGHUP, &savehup, NULL); + (void)sigaction(SIGQUIT, &savequit, NULL); + (void)sigaction(SIGTERM, &saveterm, NULL); + (void)sigaction(SIGTSTP, &savetstp, NULL); + (void)sigaction(SIGTTIN, &savettin, NULL); + (void)sigaction(SIGTTOU, &savettou, NULL); + if (input != STDIN_FILENO) + (void)close(input); + + /* + * If we were interrupted by a signal, resend it to ourselves + * now that we have restored the signal handlers. + */ + if (signo) { + kill(getpid(), signo); + switch (signo) { + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: + signo = 0; + goto restart; + } + } + + errno = save_errno; + return(nr == -1 ? NULL : buf); +} +#endif /* HAVE_READPASSPHRASE */ + +#if 0 +char * +getpass(const char *prompt) +{ + static char buf[_PASSWORD_LEN + 1]; + + return(readpassphrase(prompt, buf, sizeof(buf), RPP_ECHO_OFF)); +} +#endif + +static void handler(int s) +{ + + signo = s; +} diff --git a/encfs/readpassphrase.h b/encfs/readpassphrase.h new file mode 100644 index 0000000..e17db34 --- /dev/null +++ b/encfs/readpassphrase.h @@ -0,0 +1,52 @@ +/* $OpenBSD: readpassphrase.h,v 1.1 2000/11/21 00:48:38 millert Exp $ */ + +/* + * Copyright (c) 2000 Todd C. Miller + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _READPASSPHRASE_H_ +#define _READPASSPHRASE_H_ + +//#include "includes.h" +#include + +#ifndef HAVE_READPASSPHRASE + +#define RPP_ECHO_OFF 0x00 /* Turn off echo (default). */ +#define RPP_ECHO_ON 0x01 /* Leave echo on. */ +#define RPP_REQUIRE_TTY 0x02 /* Fail if there is no tty. */ +#define RPP_FORCELOWER 0x04 /* Force input to lower case. */ +#define RPP_FORCEUPPER 0x08 /* Force input to upper case. */ +#define RPP_SEVENBIT 0x10 /* Strip the high bit from input. */ + +#ifdef __cplusplus +extern "C" +#endif +char *readpassphrase(const char *prompt, char *buf, size_t bufSize, int flags); + +#endif /* HAVE_READPASSPHRASE */ + +#endif /* !_READPASSPHRASE_H_ */ diff --git a/encfs/test.cpp b/encfs/test.cpp new file mode 100644 index 0000000..2515281 --- /dev/null +++ b/encfs/test.cpp @@ -0,0 +1,543 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2003, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + * + */ + +#include "encfs.h" + +#include "config.h" + +#include + +#include + +#include "Cipher.h" +#include "DirNode.h" +#include "MemoryPool.h" +#include "Interface.h" +#include "FileUtils.h" +#include "ConfigReader.h" +#include "StreamNameIO.h" +#include "BlockNameIO.h" +#include "NullNameIO.h" + +#include +#include +#include +#include + +#ifdef HAVE_SSL +#define NO_DES +#include +#ifndef OPENSSL_NO_ENGINE +#include +#endif +#endif + + +using namespace std; +using namespace rel; +using namespace rlog; + +using boost::shared_ptr; + +const int FSBlockSize = 256; + +static +int checkErrorPropogation( const shared_ptr &cipher, + int size, int byteToChange, const CipherKey &key ) +{ + MemBlock orig = MemoryPool::allocate(size); + MemBlock data = MemoryPool::allocate(size); + + for(int i=0; istreamEncode( data.data, size, 0, key ); + else + cipher->blockEncode( data.data, size, 0, key ); + + // intoduce an error in the encoded data, so we can check error propogation + if(byteToChange >= 0 && byteToChange < size) + { + unsigned char previousValue = data.data[byteToChange]; + do + { + data.data[byteToChange] = rand(); + } while(data.data[byteToChange] == previousValue); + } + + if(size != FSBlockSize) + cipher->streamDecode( data.data, size, 0, key ); + else + cipher->blockDecode( data.data, size, 0, key ); + + int numByteErrors = 0; + for(int i=0; i \"" << encName.c_str() << "\""; + + // decrypt name + string decName = dirNode.plainPath( encName.c_str() ); + + if(decName == *orig) + { + if(verbose) + cerr << " OK\n"; + } else + { + if(verbose) + cerr << " FAILED (got " << decName << ")\n"; + return false; + } + + orig++; + } + + return true; +} + +bool runTests(const shared_ptr &cipher, bool verbose) +{ + // create a random key + if(verbose) + cerr << "Generating new key, output will be different on each run\n\n"; + CipherKey key = cipher->newRandomKey(); + + if(verbose) + cerr << "Testing key save / restore :"; + { + CipherKey encodingKey = cipher->newRandomKey(); + int encodedKeySize = cipher->encodedKeySize(); + unsigned char *keyBuf = new unsigned char [ encodedKeySize ]; + + cipher->writeKey( key, keyBuf, encodingKey ); + CipherKey key2 = cipher->readKey( keyBuf, encodingKey ); + if(!key2) + { + if(verbose) + cerr << " FAILED (decode error)\n"; + return false; + } + + if(cipher->compareKey( key, key2 )) + { + if(verbose) + cerr << " OK\n"; + } else + { + if(verbose) + cerr << " FAILED\n"; + return false; + } + + delete[] keyBuf; + } + + if(verbose) + cerr << "Testing Config interface load / store :"; + { + CipherKey encodingKey = cipher->newRandomKey(); + int encodedKeySize = cipher->encodedKeySize(); + unsigned char *keyBuf = new unsigned char [ encodedKeySize ]; + + cipher->writeKey( key, keyBuf, encodingKey ); + + // store in config struct.. + EncFSConfig cfg; + cfg.cipherIface = cipher->interface(); + cfg.keySize = cipher->keySize(); + cfg.blockSize = FSBlockSize; + cfg.keyData.assign( (char*)keyBuf, encodedKeySize ); + + // save config + ConfigReader cfgRdrOut; + cfgRdrOut["cipher"] << cfg.cipherIface; + cfgRdrOut["keySize"] << cfg.keySize; + cfgRdrOut["blockSize"] << cfg.blockSize; + cfgRdrOut["keyData"] << cfg.keyData; + + // save to string data.. + ConfigVar storage = cfgRdrOut.toVar(); + + // read back in and check everything.. + ConfigReader cfgRdrIn; + cfgRdrIn.loadFromVar( storage ); + + EncFSConfig cfg2; + cfgRdrIn["cipher"] >> cfg2.cipherIface; + cfgRdrIn["keySize"] >> cfg2.keySize; + cfgRdrIn["blockSize"] >> cfg2.blockSize; + cfgRdrIn["keyData"] >> cfg2.keyData; + + // check.. + rAssert( cfg.cipherIface.implements(cfg2.cipherIface) ); + rAssert( cfg.keySize == cfg2.keySize ); + rAssert( cfg.blockSize == cfg2.blockSize ); + rAssert( cfg.keyData == cfg2.keyData ); + + // try decoding key.. + + CipherKey key2 = cipher->readKey( (unsigned char*)cfg2.keyData.data(), + encodingKey ); + if(!key2) + { + if(verbose) + cerr << " FAILED (decode error)\n"; + return false; + } + + if(cipher->compareKey( key, key2 )) + { + if(verbose) + cerr << " OK\n"; + } else + { + if(verbose) + cerr << " FAILED\n"; + return false; + } + + delete[] keyBuf; + } + + shared_ptr dnConfig( new DirNode::Config ); + dnConfig->cipher = cipher; + dnConfig->key = key; + dnConfig->blockSize = FSBlockSize; + + if(verbose) + cerr << "Testing name encode/decode (stream coding w/ IV chaining)\n"; + { + dnConfig->inactivityTimer = false; + dnConfig->blockMACBytes = 0; + dnConfig->blockMACRandBytes = 0; + dnConfig->uniqueIV = false; + dnConfig->nameCoding = shared_ptr( new StreamNameIO( + StreamNameIO::CurrentInterface(), cipher, key ) ); + dnConfig->nameCoding->setChainedNameIV( true ); + + DirNode dirNode( NULL, TEST_ROOTDIR, dnConfig ); + + if(!testNameCoding( dirNode, verbose )) + return false; + } + + if(verbose) + cerr << "Testing name encode/decode (block coding w/ IV chaining)\n"; + { + dnConfig->inactivityTimer = false; + dnConfig->blockMACBytes = 0; + dnConfig->blockMACRandBytes = 0; + dnConfig->uniqueIV = false; + dnConfig->nameCoding = shared_ptr( new BlockNameIO( + BlockNameIO::CurrentInterface(), cipher, key, + cipher->cipherBlockSize() ) ); + dnConfig->nameCoding->setChainedNameIV( true ); + + DirNode dirNode( NULL, TEST_ROOTDIR, dnConfig ); + + if(!testNameCoding( dirNode, verbose )) + return false; + } + + if(!verbose) + { + { + // test stream mode, this time without IV chaining + dnConfig->inactivityTimer = false; + dnConfig->blockMACBytes = 0; + dnConfig->blockMACRandBytes = 0; + dnConfig->uniqueIV = false; + dnConfig->nameCoding = + shared_ptr( new StreamNameIO( + StreamNameIO::CurrentInterface(), cipher, key ) ); + dnConfig->nameCoding->setChainedNameIV( false ); + + DirNode dirNode( NULL, TEST_ROOTDIR, dnConfig ); + + if(!testNameCoding( dirNode, verbose )) + return false; + } + + { + // test block mode, this time without IV chaining + dnConfig->inactivityTimer = false; + dnConfig->blockMACBytes = 0; + dnConfig->blockMACRandBytes = 0; + dnConfig->uniqueIV = false; + dnConfig->nameCoding = shared_ptr( new BlockNameIO( + BlockNameIO::CurrentInterface(), cipher, key, + cipher->cipherBlockSize() ) ); + dnConfig->nameCoding->setChainedNameIV( false ); + + DirNode dirNode( NULL, TEST_ROOTDIR, dnConfig ); + + if(!testNameCoding( dirNode, verbose )) + return false; + } + } + + if(verbose) + cerr << "Testing block encode/decode on full block - "; + { + int numErrors = checkErrorPropogation( cipher, + FSBlockSize, -1, key ); + if(numErrors) + { + if(verbose) + cerr << " FAILED!\n"; + return false; + } else + { + if(verbose) + cerr << " OK\n"; + } + } + if(verbose) + cerr << "Testing block encode/decode on partial block - "; + { + int numErrors = checkErrorPropogation( cipher, + FSBlockSize-1, -1, key ); + if(numErrors) + { + if(verbose) + cerr << " FAILED!\n"; + return false; + } else + { + if(verbose) + cerr << " OK\n"; + } + } + + if(verbose) + cerr << "Checking error propogation in partial block:\n"; + { + int minChanges = FSBlockSize-1; + int maxChanges = 0; + int minAt = 0; + int maxAt = 0; + for(int i=0; i maxChanges) + { + maxChanges = numErrors; + maxAt = i; + } + } + + if(verbose) + { + cerr << "modification of 1 byte affected between " << minChanges + << " and " << maxChanges << " decoded bytes\n"; + cerr << "minimum change at byte " << minAt + << " and maximum at byte " << maxAt << "\n"; + } + } + if(verbose) + cerr << "Checking error propogation on full block:\n"; + { + int minChanges = FSBlockSize; + int maxChanges = 0; + int minAt = 0; + int maxAt = 0; + for(int i=0; i maxChanges) + { + maxChanges = numErrors; + maxAt = i; + } + } + + if(verbose) + { + cerr << "modification of 1 byte affected between " << minChanges + << " and " << maxChanges << " decoded bytes\n"; + cerr << "minimum change at byte " << minAt + << " and maximum at byte " << maxAt << "\n"; + } + } + + return true; +} + + +int main(int argc, char *argv[]) +{ + RLogInit( argc, argv ); + + StdioNode stdLog( STDERR_FILENO ); + stdLog.subscribeTo( RLOG_CHANNEL("error") ); + stdLog.subscribeTo( RLOG_CHANNEL("warning") ); +#ifndef NO_DEBUG + stdLog.subscribeTo( RLOG_CHANNEL("debug") ); +#endif + +#ifdef HAVE_SSL + SSL_load_error_strings(); + SSL_library_init(); + +#ifndef OPENSSL_NO_ENGINE + ENGINE_load_builtin_engines(); + ENGINE_register_all_ciphers(); + ENGINE_register_all_digests(); + ENGINE_register_all_RAND(); +#endif +#endif + + srand( time(0) ); + + // get a list of the available algorithms + std::list algorithms = + Cipher::GetAlgorithmList(); + std::list::const_iterator it; + cerr << "Supported Crypto interfaces:\n"; + for(it = algorithms.begin(); it != algorithms.end(); ++it) + { + cerr << it->name + << " ( " << it->iface.name() << " " + << it->iface.current() << ":" + << it->iface.revision() << ":" + << it->iface.age() << " ) : " << it->description << "\n"; + cerr << " - key length " << it->keyLength.min() << " to " + << it->keyLength.max() << " , block size " << it->blockSize.min() + << " to " << it->blockSize.max() << "\n"; + } + cerr << "\n"; + + cerr << "Testing interfaces\n"; + for(it = algorithms.begin(); it != algorithms.end(); ++it) + { + int blockSize = it->blockSize.closest( 256 ); + for(int keySize = it->keyLength.min(); keySize <= it->keyLength.max(); + keySize += it->keyLength.inc()) + { + cerr << it->name << ", key length " << keySize + << ", block size " << blockSize << ": "; + + shared_ptr cipher = Cipher::New( it->name, keySize ); + if(!cipher) + { + cerr << "FAILED TO CREATE\n"; + } else + { + try + { + if(runTests( cipher, false )) + cerr << "OK\n"; + else + cerr << "FAILED\n"; + } catch( rlog::Error &er ) + { + cerr << "Error: " << er.what() << "\n"; + } + } + } + } + + // run one test with verbose output too.. + shared_ptr cipher = Cipher::New("AES", 192); + if(!cipher) + { + cerr << "\nNo AES cipher found, skipping verbose test.\n"; + } else + { + cerr << "\nVerbose output for " << cipher->interface().name() + << " test, key length " << cipher->keySize()*8 << ", block size " + << FSBlockSize << ":\n"; + + runTests( cipher, true ); + } + + MemoryPool::destroyAll(); + + return 0; +} + + + diff --git a/encfs/testextpass b/encfs/testextpass new file mode 100644 index 0000000..5952e8f --- /dev/null +++ b/encfs/testextpass @@ -0,0 +1,45 @@ +#!/usr/bin/perl -w + +# Sample password prompt program. Demonstration of how to interface with +# encfs's --extpass option. Note that encfs's extpass interface was chosen to +# work with existing password prompt programs, like ssh-askpass. +# +# The external password program is expected to somehow prompt for the password +# and return the password on stdout. Encfs also provides some extra +# information which is stored in environment variables. +# +# Quick Start: The only necessary part of this script for encfs operation is +# the final part which prints the password to stdout. +# +# Encfs records some environment variables with useful information: +# "encfs_root" holds a path to the raw encrypted data. +# "encfs_stdout" holds a file descriptor number which will display data to +# standard output (because standard output is used for the password). +# "encfs_stderr" holds a file descriptor number which for standard error.. + +use strict; +use IO::Handle; + +# find out where we can send data to display to the user (assuming encfs was +# started in a terminal!) +my $realOut = $ENV{"encfs_stdout"} || fileno(STDOUT); +#my $realErr = $ENV{"encfs_stderr"} || fileno(STDERR); +#my $rootDir = $ENV{"encfs_root"} || "[unknown]"; +#system("xmessage realOut=$realOut, err=$realErr"); + +# for grins, show all encfs related environment variables to encfs's standard +# output +my $io = new IO::Handle; +if($io->fdopen($realOut, "w")) +{ + while (my ($key,$value) = each %ENV) + { + $io->print("$key=$value\n") if($key=~/encfs/); + } +} +$io->close(); + +## XXX XXX - This is the only part necessary for encfs to be happy. +# the functional part -- print the encfs password to stdout +print "test\n"; + diff --git a/ltmain.sh b/ltmain.sh new file mode 100644 index 0000000..a6d78e6 --- /dev/null +++ b/ltmain.sh @@ -0,0 +1,6402 @@ +# ltmain.sh - Provide generalized library-building support services. +# NOTE: Changing this file will not affect anything until you rerun configure. +# +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004 +# Free Software Foundation, Inc. +# Originally by Gordon Matzigkeit , 1996 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +basename="s,^.*/,,g" + +# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh +# is ksh but when the shell is invoked as "sh" and the current value of +# the _XPG environment variable is not equal to 1 (one), the special +# positional parameter $0, within a function call, is the name of the +# function. +progpath="$0" + +# The name of this program: +progname=`echo "$progpath" | $SED $basename` +modename="$progname" + +# Global variables: +EXIT_SUCCESS=0 +EXIT_FAILURE=1 + +PROGRAM=ltmain.sh +PACKAGE=libtool +VERSION=1.5.8 +TIMESTAMP=" (1.1220.2.118 2004/08/07 12:24:38)" + +# See if we are running on zsh, and set the options which allow our +# commands through without removal of \ escapes. +if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi + +# Check that we have a working $echo. +if test "X$1" = X--no-reexec; then + # Discard the --no-reexec flag, and continue. + shift +elif test "X$1" = X--fallback-echo; then + # Avoid inline document here, it may be left over + : +elif test "X`($echo '\t') 2>/dev/null`" = 'X\t'; then + # Yippee, $echo works! + : +else + # Restart under the correct shell, and then maybe $echo will work. + exec $SHELL "$progpath" --no-reexec ${1+"$@"} +fi + +if test "X$1" = X--fallback-echo; then + # used as fallback echo + shift + cat <&2 + $echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2 + exit $EXIT_FAILURE +fi + +# Global variables. +mode=$default_mode +nonopt= +prev= +prevopt= +run= +show="$echo" +show_help= +execute_dlfiles= +lo2o="s/\\.lo\$/.${objext}/" +o2lo="s/\\.${objext}\$/.lo/" + +##################################### +# Shell function definitions: +# This seems to be the best place for them + +# func_win32_libid arg +# return the library type of file 'arg' +# +# Need a lot of goo to handle *both* DLLs and import libs +# Has to be a shell function in order to 'eat' the argument +# that is supplied when $file_magic_command is called. +func_win32_libid () { + win32_libid_type="unknown" + win32_fileres=`file -L $1 2>/dev/null` + case $win32_fileres in + *ar\ archive\ import\ library*) # definitely import + win32_libid_type="x86 archive import" + ;; + *ar\ archive*) # could be an import, or static + if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | \ + $EGREP -e 'file format pe-i386(.*architecture: i386)?' >/dev/null ; then + win32_nmres=`eval $NM -f posix -A $1 | \ + sed -n -e '1,100{/ I /{x;/import/!{s/^/import/;h;p;};x;};}'` + if test "X$win32_nmres" = "Ximport" ; then + win32_libid_type="x86 archive import" + else + win32_libid_type="x86 archive static" + fi + fi + ;; + *DLL*) + win32_libid_type="x86 DLL" + ;; + *executable*) # but shell scripts are "executable" too... + case $win32_fileres in + *MS\ Windows\ PE\ Intel*) + win32_libid_type="x86 DLL" + ;; + esac + ;; + esac + $echo $win32_libid_type +} + + +# func_infer_tag arg +# Infer tagged configuration to use if any are available and +# if one wasn't chosen via the "--tag" command line option. +# Only attempt this if the compiler in the base compile +# command doesn't match the default compiler. +# arg is usually of the form 'gcc ...' +func_infer_tag () { + if test -n "$available_tags" && test -z "$tagname"; then + CC_quoted= + for arg in $CC; do + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + CC_quoted="$CC_quoted $arg" + done + case $@ in + # Blanks in the command may have been stripped by the calling shell, + # but not from the CC environment variable when configure was run. + " $CC "* | "$CC "* | " `$echo $CC` "* | "`$echo $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$echo $CC_quoted` "* | "`$echo $CC_quoted` "*) ;; + # Blanks at the start of $base_compile will cause this to fail + # if we don't check for them as well. + *) + for z in $available_tags; do + if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then + # Evaluate the configuration. + eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" + CC_quoted= + for arg in $CC; do + # Double-quote args containing other shell metacharacters. + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + CC_quoted="$CC_quoted $arg" + done + case "$@ " in + " $CC "* | "$CC "* | " `$echo $CC` "* | "`$echo $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$echo $CC_quoted` "* | "`$echo $CC_quoted` "*) + # The compiler in the base compile command matches + # the one in the tagged configuration. + # Assume this is the tagged configuration we want. + tagname=$z + break + ;; + esac + fi + done + # If $tagname still isn't set, then no tagged configuration + # was found and let the user know that the "--tag" command + # line option must be used. + if test -z "$tagname"; then + $echo "$modename: unable to infer tagged configuration" + $echo "$modename: specify a tag with \`--tag'" 1>&2 + exit $EXIT_FAILURE +# else +# $echo "$modename: using $tagname tagged configuration" + fi + ;; + esac + fi +} + + +# func_extract_archives gentop oldlib ... +func_extract_archives () { + my_gentop="$1"; shift + my_oldlibs=${1+"$@"} + my_oldobjs="" + my_xlib="" + my_xabs="" + my_xdir="" + my_status="" + + $show "${rm}r $my_gentop" + $run ${rm}r "$my_gentop" + $show "$mkdir $my_gentop" + $run $mkdir "$my_gentop" + my_status=$? + if test "$my_status" -ne 0 && test ! -d "$my_gentop"; then + exit $my_status + fi + + for my_xlib in $my_oldlibs; do + # Extract the objects. + case $my_xlib in + [\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;; + *) my_xabs=`pwd`"/$my_xlib" ;; + esac + my_xlib=`$echo "X$my_xlib" | $Xsed -e 's%^.*/%%'` + my_xdir="$my_gentop/$my_xlib" + + $show "${rm}r $my_xdir" + $run ${rm}r "$my_xdir" + $show "$mkdir $my_xdir" + $run $mkdir "$my_xdir" + status=$? + if test "$status" -ne 0 && test ! -d "$my_xdir"; then + exit $status + fi + case $host in + *-darwin*) + $show "Extracting $my_xabs" + # Do not bother doing anything if just a dry run + if test -z "$run"; then + darwin_orig_dir=`pwd` + cd $my_xdir || exit $? + darwin_archive=$my_xabs + darwin_curdir=`pwd` + darwin_base_archive=`basename $darwin_archive` + darwin_arches=`lipo -info "$darwin_archive" 2>/dev/null | $EGREP Architectures 2>/dev/null` + if test -n "$darwin_arches"; then + darwin_arches=`echo "$darwin_arches" | $SED -e 's/.*are://'` + darwin_arch= + $show "$darwin_base_archive has multiple architectures $darwin_arches" + for darwin_arch in $darwin_arches ; do + mkdir -p "unfat-$$/${darwin_base_archive}-${darwin_arch}" + lipo -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}" + # Remove the table of contents from the thin files. + $AR -d "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" __.SYMDEF 2>/dev/null || true + $AR -d "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" __.SYMDEF\ SORTED 2>/dev/null || true + cd "unfat-$$/${darwin_base_archive}-${darwin_arch}" + $AR -xo "${darwin_base_archive}" + rm "${darwin_base_archive}" + cd "$darwin_curdir" + done # $darwin_arches + ## Okay now we have a bunch of thin objects, gotta fatten them up :) + darwin_filelist=`find unfat-$$ -type f | xargs basename | sort -u | $NL2SP` + darwin_file= + darwin_files= + for darwin_file in $darwin_filelist; do + darwin_files=`find unfat-$$ -name $darwin_file -print | $NL2SP` + lipo -create -output "$darwin_file" $darwin_files + done # $darwin_filelist + rm -rf unfat-$$ + cd "$darwin_orig_dir" + else + cd $darwin_orig_dir + (cd $my_xdir && $AR x $my_xabs) || exit $? + fi # $darwin_arches + fi # $run + ;; + *) + # We will extract separately just the conflicting names and we will + # no longer touch any unique names. It is faster to leave these + # extract automatically by $AR in one run. + $show "(cd $my_xdir && $AR x $my_xabs)" + $run eval "(cd \$my_xdir && $AR x \$my_xabs)" || exit $? + if ($AR t "$my_xabs" | sort | sort -uc >/dev/null 2>&1); then + : + else + $echo "$modename: warning: object name conflicts; renaming object files" 1>&2 + $echo "$modename: warning: to ensure that they will not overwrite" 1>&2 + $AR t "$my_xabs" | sort | uniq -cd | while read -r count name + do + i=1 + while test "$i" -le "$count" + do + # Put our $i before any first dot (extension) + # Never overwrite any file + name_to="$name" + while test "X$name_to" = "X$name" || test -f "$my_xdir/$name_to" + do + name_to=`$echo "X$name_to" | $Xsed -e "s/\([^.]*\)/\1-$i/"` + done + $show "(cd $my_xdir && $AR xN $i $my_xabs '$name' && $mv '$name' '$name_to')" + $run eval "(cd \$my_xdir && $AR xN $i \$my_xabs '$name' && $mv '$name' '$name_to')" || exit $? + i=`expr $i + 1` + done + done + fi + ;; + esac + my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | $NL2SP` + done + + func_extract_archives_result="$my_oldobjs" +} +# End of Shell function definitions +##################################### + +# Darwin sucks +eval std_shrext=\"$shrext_cmds\" + +# Parse our command line options once, thoroughly. +while test "$#" -gt 0 +do + arg="$1" + shift + + case $arg in + -*=*) optarg=`$echo "X$arg" | $Xsed -e 's/[-_a-zA-Z0-9]*=//'` ;; + *) optarg= ;; + esac + + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + case $prev in + execute_dlfiles) + execute_dlfiles="$execute_dlfiles $arg" + ;; + tag) + tagname="$arg" + preserve_args="${preserve_args}=$arg" + + # Check whether tagname contains only valid characters + case $tagname in + *[!-_A-Za-z0-9,/]*) + $echo "$progname: invalid tag name: $tagname" 1>&2 + exit $EXIT_FAILURE + ;; + esac + + case $tagname in + CC) + # Don't test for the "default" C tag, as we know, it's there, but + # not specially marked. + ;; + *) + if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$" < "$progpath" > /dev/null; then + taglist="$taglist $tagname" + # Evaluate the configuration. + eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$tagname'$/,/^# ### END LIBTOOL TAG CONFIG: '$tagname'$/p' < $progpath`" + else + $echo "$progname: ignoring unknown tag $tagname" 1>&2 + fi + ;; + esac + ;; + *) + eval "$prev=\$arg" + ;; + esac + + prev= + prevopt= + continue + fi + + # Have we seen a non-optional argument yet? + case $arg in + --help) + show_help=yes + ;; + + --version) + $echo "$PROGRAM (GNU $PACKAGE) $VERSION$TIMESTAMP" + $echo + $echo "Copyright (C) 2003 Free Software Foundation, Inc." + $echo "This is free software; see the source for copying conditions. There is NO" + $echo "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + exit $EXIT_SUCCESS + ;; + + --config) + ${SED} -e '1,/^# ### BEGIN LIBTOOL CONFIG/d' -e '/^# ### END LIBTOOL CONFIG/,$d' $progpath + # Now print the configurations for the tags. + for tagname in $taglist; do + ${SED} -n -e "/^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$/,/^# ### END LIBTOOL TAG CONFIG: $tagname$/p" < "$progpath" + done + exit $EXIT_SUCCESS + ;; + + --debug) + $echo "$progname: enabling shell trace mode" + set -x + preserve_args="$preserve_args $arg" + ;; + + --dry-run | -n) + run=: + ;; + + --features) + $echo "host: $host" + if test "$build_libtool_libs" = yes; then + $echo "enable shared libraries" + else + $echo "disable shared libraries" + fi + if test "$build_old_libs" = yes; then + $echo "enable static libraries" + else + $echo "disable static libraries" + fi + exit $EXIT_SUCCESS + ;; + + --finish) mode="finish" ;; + + --mode) prevopt="--mode" prev=mode ;; + --mode=*) mode="$optarg" ;; + + --preserve-dup-deps) duplicate_deps="yes" ;; + + --quiet | --silent) + show=: + preserve_args="$preserve_args $arg" + ;; + + --tag) prevopt="--tag" prev=tag ;; + --tag=*) + set tag "$optarg" ${1+"$@"} + shift + prev=tag + preserve_args="$preserve_args --tag" + ;; + + -dlopen) + prevopt="-dlopen" + prev=execute_dlfiles + ;; + + -*) + $echo "$modename: unrecognized option \`$arg'" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + ;; + + *) + nonopt="$arg" + break + ;; + esac +done + +if test -n "$prevopt"; then + $echo "$modename: option \`$prevopt' requires an argument" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE +fi + +# If this variable is set in any of the actions, the command in it +# will be execed at the end. This prevents here-documents from being +# left over by shells. +exec_cmd= + +if test -z "$show_help"; then + + # Infer the operation mode. + if test -z "$mode"; then + $echo "*** Warning: inferring the mode of operation is deprecated." 1>&2 + $echo "*** Future versions of Libtool will require -mode=MODE be specified." 1>&2 + case $nonopt in + *cc | cc* | *++ | gcc* | *-gcc* | g++* | xlc*) + mode=link + for arg + do + case $arg in + -c) + mode=compile + break + ;; + esac + done + ;; + *db | *dbx | *strace | *truss) + mode=execute + ;; + *install*|cp|mv) + mode=install + ;; + *rm) + mode=uninstall + ;; + *) + # If we have no mode, but dlfiles were specified, then do execute mode. + test -n "$execute_dlfiles" && mode=execute + + # Just use the default operation mode. + if test -z "$mode"; then + if test -n "$nonopt"; then + $echo "$modename: warning: cannot infer operation mode from \`$nonopt'" 1>&2 + else + $echo "$modename: warning: cannot infer operation mode without MODE-ARGS" 1>&2 + fi + fi + ;; + esac + fi + + # Only execute mode is allowed to have -dlopen flags. + if test -n "$execute_dlfiles" && test "$mode" != execute; then + $echo "$modename: unrecognized option \`-dlopen'" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + # Change the help message to a mode-specific one. + generic_help="$help" + help="Try \`$modename --help --mode=$mode' for more information." + + # These modes are in order of execution frequency so that they run quickly. + case $mode in + # libtool compile mode + compile) + modename="$modename: compile" + # Get the compilation command and the source file. + base_compile= + srcfile="$nonopt" # always keep a non-empty value in "srcfile" + suppress_opt=yes + suppress_output= + arg_mode=normal + libobj= + later= + + for arg + do + case "$arg_mode" in + arg ) + # do not "continue". Instead, add this to base_compile + lastarg="$arg" + arg_mode=normal + ;; + + target ) + libobj="$arg" + arg_mode=normal + continue + ;; + + normal ) + # Accept any command-line options. + case $arg in + -o) + if test -n "$libobj" ; then + $echo "$modename: you cannot specify \`-o' more than once" 1>&2 + exit $EXIT_FAILURE + fi + arg_mode=target + continue + ;; + + -static | -prefer-pic | -prefer-non-pic) + later="$later $arg" + continue + ;; + + -no-suppress) + suppress_opt=no + continue + ;; + + -Xcompiler) + arg_mode=arg # the next one goes into the "base_compile" arg list + continue # The current "srcfile" will either be retained or + ;; # replaced later. I would guess that would be a bug. + + -Wc,*) + args=`$echo "X$arg" | $Xsed -e "s/^-Wc,//"` + lastarg= + save_ifs="$IFS"; IFS=',' + for arg in $args; do + IFS="$save_ifs" + + # Double-quote args containing other shell metacharacters. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + lastarg="$lastarg $arg" + done + IFS="$save_ifs" + lastarg=`$echo "X$lastarg" | $Xsed -e "s/^ //"` + + # Add the arguments to base_compile. + base_compile="$base_compile $lastarg" + continue + ;; + + * ) + # Accept the current argument as the source file. + # The previous "srcfile" becomes the current argument. + # + lastarg="$srcfile" + srcfile="$arg" + ;; + esac # case $arg + ;; + esac # case $arg_mode + + # Aesthetically quote the previous argument. + lastarg=`$echo "X$lastarg" | $Xsed -e "$sed_quote_subst"` + + case $lastarg in + # Double-quote args containing other shell metacharacters. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + lastarg="\"$lastarg\"" + ;; + esac + + base_compile="$base_compile $lastarg" + done # for arg + + case $arg_mode in + arg) + $echo "$modename: you must specify an argument for -Xcompile" + exit $EXIT_FAILURE + ;; + target) + $echo "$modename: you must specify a target with \`-o'" 1>&2 + exit $EXIT_FAILURE + ;; + *) + # Get the name of the library object. + [ -z "$libobj" ] && libobj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%'` + ;; + esac + + # Recognize several different file suffixes. + # If the user specifies -o file.o, it is replaced with file.lo + xform='[cCFSifmso]' + case $libobj in + *.ada) xform=ada ;; + *.adb) xform=adb ;; + *.ads) xform=ads ;; + *.asm) xform=asm ;; + *.c++) xform=c++ ;; + *.cc) xform=cc ;; + *.ii) xform=ii ;; + *.class) xform=class ;; + *.cpp) xform=cpp ;; + *.cxx) xform=cxx ;; + *.f90) xform=f90 ;; + *.for) xform=for ;; + *.java) xform=java ;; + esac + + libobj=`$echo "X$libobj" | $Xsed -e "s/\.$xform$/.lo/"` + + case $libobj in + *.lo) obj=`$echo "X$libobj" | $Xsed -e "$lo2o"` ;; + *) + $echo "$modename: cannot determine name of library object from \`$libobj'" 1>&2 + exit $EXIT_FAILURE + ;; + esac + + func_infer_tag $base_compile + + for arg in $later; do + case $arg in + -static) + build_old_libs=yes + continue + ;; + + -prefer-pic) + pic_mode=yes + continue + ;; + + -prefer-non-pic) + pic_mode=no + continue + ;; + esac + done + + objname=`$echo "X$obj" | $Xsed -e 's%^.*/%%'` + xdir=`$echo "X$obj" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$obj"; then + xdir= + else + xdir=$xdir/ + fi + lobj=${xdir}$objdir/$objname + + if test -z "$base_compile"; then + $echo "$modename: you must specify a compilation command" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + # Delete any leftover library objects. + if test "$build_old_libs" = yes; then + removelist="$obj $lobj $libobj ${libobj}T" + else + removelist="$lobj $libobj ${libobj}T" + fi + + $run $rm $removelist + trap "$run $rm $removelist; exit $EXIT_FAILURE" 1 2 15 + + # On Cygwin there's no "real" PIC flag so we must build both object types + case $host_os in + cygwin* | mingw* | pw32* | os2*) + pic_mode=default + ;; + esac + if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then + # non-PIC code in shared libraries is not supported + pic_mode=default + fi + + # Calculate the filename of the output object if compiler does + # not support -o with -c + if test "$compiler_c_o" = no; then + output_obj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%' -e 's%\.[^.]*$%%'`.${objext} + lockfile="$output_obj.lock" + removelist="$removelist $output_obj $lockfile" + trap "$run $rm $removelist; exit $EXIT_FAILURE" 1 2 15 + else + output_obj= + need_locks=no + lockfile= + fi + + # Lock this critical section if it is needed + # We use this script file to make the link, it avoids creating a new file + if test "$need_locks" = yes; then + until $run ln "$progpath" "$lockfile" 2>/dev/null; do + $show "Waiting for $lockfile to be removed" + sleep 2 + done + elif test "$need_locks" = warn; then + if test -f "$lockfile"; then + $echo "\ +*** ERROR, $lockfile exists and contains: +`cat $lockfile 2>/dev/null` + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $run $rm $removelist + exit $EXIT_FAILURE + fi + $echo $srcfile > "$lockfile" + fi + + if test -n "$fix_srcfile_path"; then + eval srcfile=\"$fix_srcfile_path\" + fi + + $run $rm "$libobj" "${libobj}T" + + # Create a libtool object file (analogous to a ".la" file), + # but don't create it if we're doing a dry run. + test -z "$run" && cat > ${libobj}T </dev/null`" != "X$srcfile"; then + $echo "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $run $rm $removelist + exit $EXIT_FAILURE + fi + + # Just move the object if needed, then go on to compile the next one + if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then + $show "$mv $output_obj $lobj" + if $run $mv $output_obj $lobj; then : + else + error=$? + $run $rm $removelist + exit $error + fi + fi + + # Append the name of the PIC object to the libtool object file. + test -z "$run" && cat >> ${libobj}T <> ${libobj}T </dev/null`" != "X$srcfile"; then + $echo "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $run $rm $removelist + exit $EXIT_FAILURE + fi + + # Just move the object if needed + if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then + $show "$mv $output_obj $obj" + if $run $mv $output_obj $obj; then : + else + error=$? + $run $rm $removelist + exit $error + fi + fi + + # Append the name of the non-PIC object the libtool object file. + # Only append if the libtool object file exists. + test -z "$run" && cat >> ${libobj}T <> ${libobj}T <&2 + fi + if test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + else + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + fi + build_libtool_libs=no + build_old_libs=yes + prefer_static_libs=yes + break + ;; + esac + done + + # See if our shared archives depend on static archives. + test -n "$old_archive_from_new_cmds" && build_old_libs=yes + + # Go through the arguments, transforming them on the way. + while test "$#" -gt 0; do + arg="$1" + shift + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + qarg=\"`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`\" ### testsuite: skip nested quoting test + ;; + *) qarg=$arg ;; + esac + libtool_args="$libtool_args $qarg" + + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + case $prev in + output) + compile_command="$compile_command @OUTPUT@" + finalize_command="$finalize_command @OUTPUT@" + ;; + esac + + case $prev in + dlfiles|dlprefiles) + if test "$preload" = no; then + # Add the symbol object into the linking commands. + compile_command="$compile_command @SYMFILE@" + finalize_command="$finalize_command @SYMFILE@" + preload=yes + fi + case $arg in + *.la | *.lo) ;; # We handle these cases below. + force) + if test "$dlself" = no; then + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + self) + if test "$prev" = dlprefiles; then + dlself=yes + elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then + dlself=yes + else + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + *) + if test "$prev" = dlfiles; then + dlfiles="$dlfiles $arg" + else + dlprefiles="$dlprefiles $arg" + fi + prev= + continue + ;; + esac + ;; + expsyms) + export_symbols="$arg" + if test ! -f "$arg"; then + $echo "$modename: symbol file \`$arg' does not exist" + exit $EXIT_FAILURE + fi + prev= + continue + ;; + expsyms_regex) + export_symbols_regex="$arg" + prev= + continue + ;; + inst_prefix) + inst_prefix_dir="$arg" + prev= + continue + ;; + precious_regex) + precious_files_regex="$arg" + prev= + continue + ;; + release) + release="-$arg" + prev= + continue + ;; + objectlist) + if test -f "$arg"; then + save_arg=$arg + moreargs= + for fil in `cat $save_arg` + do +# moreargs="$moreargs $fil" + arg=$fil + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if (${SED} -e '2q' $arg | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + pic_object= + non_pic_object= + + # Read the .lo file + # If there is no directory component, then add one. + case $arg in + */* | *\\*) . $arg ;; + *) . ./$arg ;; + esac + + if test -z "$pic_object" || \ + test -z "$non_pic_object" || + test "$pic_object" = none && \ + test "$non_pic_object" = none; then + $echo "$modename: cannot find name of object for \`$arg'" 1>&2 + exit $EXIT_FAILURE + fi + + # Extract subdirectory from the argument. + xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$arg"; then + xdir= + else + xdir="$xdir/" + fi + + if test "$pic_object" != none; then + # Prepend the subdirectory the object is found in. + pic_object="$xdir$pic_object" + + if test "$prev" = dlfiles; then + if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then + dlfiles="$dlfiles $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test "$prev" = dlprefiles; then + # Preload the old-style object. + dlprefiles="$dlprefiles $pic_object" + prev= + fi + + # A PIC object. + libobjs="$libobjs $pic_object" + arg="$pic_object" + fi + + # Non-PIC object. + if test "$non_pic_object" != none; then + # Prepend the subdirectory the object is found in. + non_pic_object="$xdir$non_pic_object" + + # A standard non-PIC object + non_pic_objects="$non_pic_objects $non_pic_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + fi + else + # Only an error if not doing a dry-run. + if test -z "$run"; then + $echo "$modename: \`$arg' is not a valid libtool object" 1>&2 + exit $EXIT_FAILURE + else + # Dry-run case. + + # Extract subdirectory from the argument. + xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$arg"; then + xdir= + else + xdir="$xdir/" + fi + + pic_object=`$echo "X${xdir}${objdir}/${arg}" | $Xsed -e "$lo2o"` + non_pic_object=`$echo "X${xdir}${arg}" | $Xsed -e "$lo2o"` + libobjs="$libobjs $pic_object" + non_pic_objects="$non_pic_objects $non_pic_object" + fi + fi + done + else + $echo "$modename: link input file \`$save_arg' does not exist" + exit $EXIT_FAILURE + fi + arg=$save_arg + prev= + continue + ;; + rpath | xrpath) + # We need an absolute path. + case $arg in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + $echo "$modename: only absolute run-paths are allowed" 1>&2 + exit $EXIT_FAILURE + ;; + esac + if test "$prev" = rpath; then + case "$rpath " in + *" $arg "*) ;; + *) rpath="$rpath $arg" ;; + esac + else + case "$xrpath " in + *" $arg "*) ;; + *) xrpath="$xrpath $arg" ;; + esac + fi + prev= + continue + ;; + xcompiler) + compiler_flags="$compiler_flags $qarg" + prev= + compile_command="$compile_command $qarg" + finalize_command="$finalize_command $qarg" + continue + ;; + xlinker) + linker_flags="$linker_flags $qarg" + compiler_flags="$compiler_flags $wl$qarg" + prev= + compile_command="$compile_command $wl$qarg" + finalize_command="$finalize_command $wl$qarg" + continue + ;; + xcclinker) + linker_flags="$linker_flags $qarg" + compiler_flags="$compiler_flags $qarg" + prev= + compile_command="$compile_command $qarg" + finalize_command="$finalize_command $qarg" + continue + ;; + shrext) + shrext_cmds="$arg" + prev= + continue + ;; + *) + eval "$prev=\"\$arg\"" + prev= + continue + ;; + esac + fi # test -n "$prev" + + prevarg="$arg" + + case $arg in + -all-static) + if test -n "$link_static_flag"; then + compile_command="$compile_command $link_static_flag" + finalize_command="$finalize_command $link_static_flag" + fi + continue + ;; + + -allow-undefined) + # FIXME: remove this flag sometime in the future. + $echo "$modename: \`-allow-undefined' is deprecated because it is the default" 1>&2 + continue + ;; + + -avoid-version) + avoid_version=yes + continue + ;; + + -dlopen) + prev=dlfiles + continue + ;; + + -dlpreopen) + prev=dlprefiles + continue + ;; + + -export-dynamic) + export_dynamic=yes + continue + ;; + + -export-symbols | -export-symbols-regex) + if test -n "$export_symbols" || test -n "$export_symbols_regex"; then + $echo "$modename: more than one -exported-symbols argument is not allowed" + exit $EXIT_FAILURE + fi + if test "X$arg" = "X-export-symbols"; then + prev=expsyms + else + prev=expsyms_regex + fi + continue + ;; + + -inst-prefix-dir) + prev=inst_prefix + continue + ;; + + # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* + # so, if we see these flags be careful not to treat them like -L + -L[A-Z][A-Z]*:*) + case $with_gcc/$host in + no/*-*-irix* | /*-*-irix*) + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + ;; + esac + continue + ;; + + -L*) + dir=`$echo "X$arg" | $Xsed -e 's/^-L//'` + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + absdir=`cd "$dir" && pwd` + if test -z "$absdir"; then + $echo "$modename: cannot determine absolute directory name of \`$dir'" 1>&2 + exit $EXIT_FAILURE + fi + dir="$absdir" + ;; + esac + case "$deplibs " in + *" -L$dir "*) ;; + *) + deplibs="$deplibs -L$dir" + lib_search_path="$lib_search_path $dir" + ;; + esac + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) + case :$dllsearchpath: in + *":$dir:"*) ;; + *) dllsearchpath="$dllsearchpath:$dir";; + esac + ;; + esac + continue + ;; + + -l*) + if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then + case $host in + *-*-cygwin* | *-*-pw32* | *-*-beos*) + # These systems don't actually have a C or math library (as such) + continue + ;; + *-*-mingw* | *-*-os2*) + # These systems don't actually have a C library (as such) + test "X$arg" = "X-lc" && continue + ;; + *-*-openbsd* | *-*-freebsd*) + # Do not include libc due to us having libc/libc_r. + test "X$arg" = "X-lc" && continue + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C and math libraries are in the System framework + deplibs="$deplibs -framework System" + continue + esac + elif test "X$arg" = "X-lc_r"; then + case $host in + *-*-openbsd* | *-*-freebsd*) + # Do not include libc_r directly, use -pthread flag. + continue + ;; + esac + fi + deplibs="$deplibs $arg" + continue + ;; + + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe) + deplibs="$deplibs $arg" + continue + ;; + + -module) + module=yes + continue + ;; + + # gcc -m* arguments should be passed to the linker via $compiler_flags + # in order to pass architecture information to the linker + # (e.g. 32 vs 64-bit). This may also be accomplished via -Wl,-mfoo + # but this is not reliable with gcc because gcc may use -mfoo to + # select a different linker, different libraries, etc, while + # -Wl,-mfoo simply passes -mfoo to the linker. + -m*) + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + if test "$with_gcc" = "yes" ; then + compiler_flags="$compiler_flags $arg" + fi + continue + ;; + + -shrext) + prev=shrext + continue + ;; + + -no-fast-install) + fast_install=no + continue + ;; + + -no-install) + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) + # The PATH hackery in wrapper scripts is required on Windows + # in order for the loader to find any dlls it needs. + $echo "$modename: warning: \`-no-install' is ignored for $host" 1>&2 + $echo "$modename: warning: assuming \`-no-fast-install' instead" 1>&2 + fast_install=no + ;; + *) no_install=yes ;; + esac + continue + ;; + + -no-undefined) + allow_undefined=no + continue + ;; + + -objectlist) + prev=objectlist + continue + ;; + + -o) prev=output ;; + + -precious-files-regex) + prev=precious_regex + continue + ;; + + -release) + prev=release + continue + ;; + + -rpath) + prev=rpath + continue + ;; + + -R) + prev=xrpath + continue + ;; + + -R*) + dir=`$echo "X$arg" | $Xsed -e 's/^-R//'` + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + $echo "$modename: only absolute run-paths are allowed" 1>&2 + exit $EXIT_FAILURE + ;; + esac + case "$xrpath " in + *" $dir "*) ;; + *) xrpath="$xrpath $dir" ;; + esac + continue + ;; + + -static) + # The effects of -static are defined in a previous loop. + # We used to do the same as -all-static on platforms that + # didn't have a PIC flag, but the assumption that the effects + # would be equivalent was wrong. It would break on at least + # Digital Unix and AIX. + continue + ;; + + -thread-safe) + thread_safe=yes + continue + ;; + + -version-info) + prev=vinfo + continue + ;; + -version-number) + prev=vinfo + vinfo_number=yes + continue + ;; + + -Wc,*) + args=`$echo "X$arg" | $Xsed -e "$sed_quote_subst" -e 's/^-Wc,//'` + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + case $flag in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + flag="\"$flag\"" + ;; + esac + arg="$arg $wl$flag" + compiler_flags="$compiler_flags $flag" + done + IFS="$save_ifs" + arg=`$echo "X$arg" | $Xsed -e "s/^ //"` + ;; + + -Wl,*) + args=`$echo "X$arg" | $Xsed -e "$sed_quote_subst" -e 's/^-Wl,//'` + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + case $flag in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + flag="\"$flag\"" + ;; + esac + arg="$arg $wl$flag" + compiler_flags="$compiler_flags $wl$flag" + linker_flags="$linker_flags $flag" + done + IFS="$save_ifs" + arg=`$echo "X$arg" | $Xsed -e "s/^ //"` + ;; + + -Xcompiler) + prev=xcompiler + continue + ;; + + -Xlinker) + prev=xlinker + continue + ;; + + -XCClinker) + prev=xcclinker + continue + ;; + + # Some other compiler flag. + -* | +*) + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + ;; + + *.$objext) + # A standard object. + objs="$objs $arg" + ;; + + *.lo) + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if (${SED} -e '2q' $arg | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + pic_object= + non_pic_object= + + # Read the .lo file + # If there is no directory component, then add one. + case $arg in + */* | *\\*) . $arg ;; + *) . ./$arg ;; + esac + + if test -z "$pic_object" || \ + test -z "$non_pic_object" || + test "$pic_object" = none && \ + test "$non_pic_object" = none; then + $echo "$modename: cannot find name of object for \`$arg'" 1>&2 + exit $EXIT_FAILURE + fi + + # Extract subdirectory from the argument. + xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$arg"; then + xdir= + else + xdir="$xdir/" + fi + + if test "$pic_object" != none; then + # Prepend the subdirectory the object is found in. + pic_object="$xdir$pic_object" + + if test "$prev" = dlfiles; then + if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then + dlfiles="$dlfiles $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test "$prev" = dlprefiles; then + # Preload the old-style object. + dlprefiles="$dlprefiles $pic_object" + prev= + fi + + # A PIC object. + libobjs="$libobjs $pic_object" + arg="$pic_object" + fi + + # Non-PIC object. + if test "$non_pic_object" != none; then + # Prepend the subdirectory the object is found in. + non_pic_object="$xdir$non_pic_object" + + # A standard non-PIC object + non_pic_objects="$non_pic_objects $non_pic_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + fi + else + # Only an error if not doing a dry-run. + if test -z "$run"; then + $echo "$modename: \`$arg' is not a valid libtool object" 1>&2 + exit $EXIT_FAILURE + else + # Dry-run case. + + # Extract subdirectory from the argument. + xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$arg"; then + xdir= + else + xdir="$xdir/" + fi + + pic_object=`$echo "X${xdir}${objdir}/${arg}" | $Xsed -e "$lo2o"` + non_pic_object=`$echo "X${xdir}${arg}" | $Xsed -e "$lo2o"` + libobjs="$libobjs $pic_object" + non_pic_objects="$non_pic_objects $non_pic_object" + fi + fi + ;; + + *.$libext) + # An archive. + deplibs="$deplibs $arg" + old_deplibs="$old_deplibs $arg" + continue + ;; + + *.la) + # A libtool-controlled library. + + if test "$prev" = dlfiles; then + # This library was specified with -dlopen. + dlfiles="$dlfiles $arg" + prev= + elif test "$prev" = dlprefiles; then + # The library was specified with -dlpreopen. + dlprefiles="$dlprefiles $arg" + prev= + else + deplibs="$deplibs $arg" + fi + continue + ;; + + # Some other compiler argument. + *) + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + ;; + esac # arg + + # Now actually substitute the argument into the commands. + if test -n "$arg"; then + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + fi + done # argument parsing loop + + if test -n "$prev"; then + $echo "$modename: the \`$prevarg' option requires an argument" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then + eval arg=\"$export_dynamic_flag_spec\" + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + fi + + oldlibs= + # calculate the name of the file, without its directory + outputname=`$echo "X$output" | $Xsed -e 's%^.*/%%'` + libobjs_save="$libobjs" + + if test -n "$shlibpath_var"; then + # get the directories listed in $shlibpath_var + eval shlib_search_path=\`\$echo \"X\${$shlibpath_var}\" \| \$Xsed -e \'s/:/ /g\'\` + else + shlib_search_path= + fi + eval sys_lib_search_path=\"$sys_lib_search_path_spec\" + eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" + + output_objdir=`$echo "X$output" | $Xsed -e 's%/[^/]*$%%'` + if test "X$output_objdir" = "X$output"; then + output_objdir="$objdir" + else + output_objdir="$output_objdir/$objdir" + fi + # Create the object directory. + if test ! -d "$output_objdir"; then + $show "$mkdir $output_objdir" + $run $mkdir $output_objdir + status=$? + if test "$status" -ne 0 && test ! -d "$output_objdir"; then + exit $status + fi + fi + + # Determine the type of output + case $output in + "") + $echo "$modename: you must specify an output file" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + ;; + *.$libext) linkmode=oldlib ;; + *.lo | *.$objext) linkmode=obj ;; + *.la) linkmode=lib ;; + *) linkmode=prog ;; # Anything else should be a program. + esac + + case $host in + *cygwin* | *mingw* | *pw32*) + # don't eliminate duplications in $postdeps and $predeps + duplicate_compiler_generated_deps=yes + ;; + *) + duplicate_compiler_generated_deps=$duplicate_deps + ;; + esac + specialdeplibs= + + libs= + # Find all interdependent deplibs by searching for libraries + # that are linked more than once (e.g. -la -lb -la) + for deplib in $deplibs; do + if test "X$duplicate_deps" = "Xyes" ; then + case "$libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + libs="$libs $deplib" + done + + if test "$linkmode" = lib; then + libs="$predeps $libs $compiler_lib_search_path $postdeps" + + # Compute libraries that are listed more than once in $predeps + # $postdeps and mark them as special (i.e., whose duplicates are + # not to be eliminated). + pre_post_deps= + if test "X$duplicate_compiler_generated_deps" = "Xyes" ; then + for pre_post_dep in $predeps $postdeps; do + case "$pre_post_deps " in + *" $pre_post_dep "*) specialdeplibs="$specialdeplibs $pre_post_deps" ;; + esac + pre_post_deps="$pre_post_deps $pre_post_dep" + done + fi + pre_post_deps= + fi + + deplibs= + newdependency_libs= + newlib_search_path= + need_relink=no # whether we're linking any uninstalled libtool libraries + notinst_deplibs= # not-installed libtool libraries + notinst_path= # paths that contain not-installed libtool libraries + case $linkmode in + lib) + passes="conv link" + for file in $dlfiles $dlprefiles; do + case $file in + *.la) ;; + *) + $echo "$modename: libraries can \`-dlopen' only libtool libraries: $file" 1>&2 + exit $EXIT_FAILURE + ;; + esac + done + ;; + prog) + compile_deplibs= + finalize_deplibs= + alldeplibs=no + newdlfiles= + newdlprefiles= + passes="conv scan dlopen dlpreopen link" + ;; + *) passes="conv" + ;; + esac + for pass in $passes; do + if test "$linkmode,$pass" = "lib,link" || + test "$linkmode,$pass" = "prog,scan"; then + libs="$deplibs" + deplibs= + fi + if test "$linkmode" = prog; then + case $pass in + dlopen) libs="$dlfiles" ;; + dlpreopen) libs="$dlprefiles" ;; + link) libs="$deplibs %DEPLIBS% $dependency_libs" ;; + esac + fi + if test "$pass" = dlopen; then + # Collect dlpreopened libraries + save_deplibs="$deplibs" + deplibs= + fi + for deplib in $libs; do + lib= + found=no + case $deplib in + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe) + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + fi + continue + ;; + -l*) + if test "$linkmode" != lib && test "$linkmode" != prog; then + $echo "$modename: warning: \`-l' is ignored for archives/objects" 1>&2 + continue + fi + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + name=`$echo "X$deplib" | $Xsed -e 's/^-l//'` + for searchdir in $newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path; do + for search_ext in .la $std_shrext .so .a; do + # Search the libtool library + lib="$searchdir/lib${name}${search_ext}" + if test -f "$lib"; then + if test "$search_ext" = ".la"; then + found=yes + else + found=no + fi + break 2 + fi + done + done + if test "$found" != yes; then + # deplib doesn't seem to be a libtool library + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" + fi + continue + else # deplib is a libtool library + # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, + # We need to do some special things here, and not later. + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $deplib "*) + if (${SED} -e '2q' $lib | + grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + library_names= + old_library= + case $lib in + */* | *\\*) . $lib ;; + *) . ./$lib ;; + esac + for l in $old_library $library_names; do + ll="$l" + done + if test "X$ll" = "X$old_library" ; then # only static version available + found=no + ladir=`$echo "X$lib" | $Xsed -e 's%/[^/]*$%%'` + test "X$ladir" = "X$lib" && ladir="." + lib=$ladir/$old_library + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" + fi + continue + fi + fi + ;; + *) ;; + esac + fi + fi + ;; # -l + -L*) + case $linkmode in + lib) + deplibs="$deplib $deplibs" + test "$pass" = conv && continue + newdependency_libs="$deplib $newdependency_libs" + newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'` + ;; + prog) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + if test "$pass" = scan; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'` + ;; + *) + $echo "$modename: warning: \`-L' is ignored for archives/objects" 1>&2 + ;; + esac # linkmode + continue + ;; # -L + -R*) + if test "$pass" = link; then + dir=`$echo "X$deplib" | $Xsed -e 's/^-R//'` + # Make sure the xrpath contains only unique directories. + case "$xrpath " in + *" $dir "*) ;; + *) xrpath="$xrpath $dir" ;; + esac + fi + deplibs="$deplib $deplibs" + continue + ;; + *.la) lib="$deplib" ;; + *.$libext) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + case $linkmode in + lib) + valid_a_lib=no + case $deplibs_check_method in + match_pattern*) + set dummy $deplibs_check_method + match_pattern_regex=`expr "$deplibs_check_method" : "$2 \(.*\)"` + if eval $echo \"$deplib\" 2>/dev/null \ + | $SED 10q \ + | $EGREP "$match_pattern_regex" > /dev/null; then + valid_a_lib=yes + fi + ;; + pass_all) + valid_a_lib=yes + ;; + esac + if test "$valid_a_lib" != yes; then + $echo + $echo "*** Warning: Trying to link with static lib archive $deplib." + $echo "*** I have the capability to make that library automatically link in when" + $echo "*** you link to this library. But I can only do this if you have a" + $echo "*** shared version of the library, which you do not appear to have" + $echo "*** because the file extensions .$libext of this argument makes me believe" + $echo "*** that it is just a static archive that I should not used here." + else + $echo + $echo "*** Warning: Linking the shared library $output against the" + $echo "*** static library $deplib is not portable!" + deplibs="$deplib $deplibs" + fi + continue + ;; + prog) + if test "$pass" != link; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + continue + ;; + esac # linkmode + ;; # *.$libext + *.lo | *.$objext) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + elif test "$linkmode" = prog; then + if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then + # If there is no dlopen support or we're linking statically, + # we need to preload. + newdlprefiles="$newdlprefiles $deplib" + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + newdlfiles="$newdlfiles $deplib" + fi + fi + continue + ;; + %DEPLIBS%) + alldeplibs=yes + continue + ;; + esac # case $deplib + if test "$found" = yes || test -f "$lib"; then : + else + $echo "$modename: cannot find the library \`$lib'" 1>&2 + exit $EXIT_FAILURE + fi + + # Check to see that this really is a libtool archive. + if (${SED} -e '2q' $lib | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : + else + $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 + exit $EXIT_FAILURE + fi + + ladir=`$echo "X$lib" | $Xsed -e 's%/[^/]*$%%'` + test "X$ladir" = "X$lib" && ladir="." + + dlname= + dlopen= + dlpreopen= + libdir= + library_names= + old_library= + # If the library was installed with an old release of libtool, + # it will not redefine variables installed, or shouldnotlink + installed=yes + shouldnotlink=no + + # Read the .la file + case $lib in + */* | *\\*) . $lib ;; + *) . ./$lib ;; + esac + + if test "$linkmode,$pass" = "lib,link" || + test "$linkmode,$pass" = "prog,scan" || + { test "$linkmode" != prog && test "$linkmode" != lib; }; then + test -n "$dlopen" && dlfiles="$dlfiles $dlopen" + test -n "$dlpreopen" && dlprefiles="$dlprefiles $dlpreopen" + fi + + if test "$pass" = conv; then + # Only check for convenience libraries + deplibs="$lib $deplibs" + if test -z "$libdir"; then + if test -z "$old_library"; then + $echo "$modename: cannot find name of link library for \`$lib'" 1>&2 + exit $EXIT_FAILURE + fi + # It is a libtool convenience library, so add in its objects. + convenience="$convenience $ladir/$objdir/$old_library" + old_convenience="$old_convenience $ladir/$objdir/$old_library" + tmp_libs= + for deplib in $dependency_libs; do + deplibs="$deplib $deplibs" + if test "X$duplicate_deps" = "Xyes" ; then + case "$tmp_libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + tmp_libs="$tmp_libs $deplib" + done + elif test "$linkmode" != prog && test "$linkmode" != lib; then + $echo "$modename: \`$lib' is not a convenience library" 1>&2 + exit $EXIT_FAILURE + fi + continue + fi # $pass = conv + + + # Get the name of the library we link against. + linklib= + for l in $old_library $library_names; do + linklib="$l" + done + if test -z "$linklib"; then + $echo "$modename: cannot find name of link library for \`$lib'" 1>&2 + exit $EXIT_FAILURE + fi + + # This library was specified with -dlopen. + if test "$pass" = dlopen; then + if test -z "$libdir"; then + $echo "$modename: cannot -dlopen a convenience library: \`$lib'" 1>&2 + exit $EXIT_FAILURE + fi + if test -z "$dlname" || + test "$dlopen_support" != yes || + test "$build_libtool_libs" = no; then + # If there is no dlname, no dlopen support or we're linking + # statically, we need to preload. We also need to preload any + # dependent libraries so libltdl's deplib preloader doesn't + # bomb out in the load deplibs phase. + dlprefiles="$dlprefiles $lib $dependency_libs" + else + newdlfiles="$newdlfiles $lib" + fi + continue + fi # $pass = dlopen + + # We need an absolute path. + case $ladir in + [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;; + *) + abs_ladir=`cd "$ladir" && pwd` + if test -z "$abs_ladir"; then + $echo "$modename: warning: cannot determine absolute directory name of \`$ladir'" 1>&2 + $echo "$modename: passing it literally to the linker, although it might fail" 1>&2 + abs_ladir="$ladir" + fi + ;; + esac + laname=`$echo "X$lib" | $Xsed -e 's%^.*/%%'` + + # Find the relevant object directory and library name. + if test "X$installed" = Xyes; then + if test ! -f "$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then + $echo "$modename: warning: library \`$lib' was moved." 1>&2 + dir="$ladir" + absdir="$abs_ladir" + libdir="$abs_ladir" + else + dir="$libdir" + absdir="$libdir" + fi + else + if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then + dir="$ladir" + absdir="$abs_ladir" + # Remove this search path later + notinst_path="$notinst_path $abs_ladir" + else + dir="$ladir/$objdir" + absdir="$abs_ladir/$objdir" + # Remove this search path later + notinst_path="$notinst_path $abs_ladir" + fi + fi # $installed = yes + name=`$echo "X$laname" | $Xsed -e 's/\.la$//' -e 's/^lib//'` + + # This library was specified with -dlpreopen. + if test "$pass" = dlpreopen; then + if test -z "$libdir"; then + $echo "$modename: cannot -dlpreopen a convenience library: \`$lib'" 1>&2 + exit $EXIT_FAILURE + fi + # Prefer using a static library (so that no silly _DYNAMIC symbols + # are required to link). + if test -n "$old_library"; then + newdlprefiles="$newdlprefiles $dir/$old_library" + # Otherwise, use the dlname, so that lt_dlopen finds it. + elif test -n "$dlname"; then + newdlprefiles="$newdlprefiles $dir/$dlname" + else + newdlprefiles="$newdlprefiles $dir/$linklib" + fi + fi # $pass = dlpreopen + + if test -z "$libdir"; then + # Link the convenience library + if test "$linkmode" = lib; then + deplibs="$dir/$old_library $deplibs" + elif test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$dir/$old_library $compile_deplibs" + finalize_deplibs="$dir/$old_library $finalize_deplibs" + else + deplibs="$lib $deplibs" # used for prog,scan pass + fi + continue + fi + + + if test "$linkmode" = prog && test "$pass" != link; then + newlib_search_path="$newlib_search_path $ladir" + deplibs="$lib $deplibs" + + linkalldeplibs=no + if test "$link_all_deplibs" != no || test -z "$library_names" || + test "$build_libtool_libs" = no; then + linkalldeplibs=yes + fi + + tmp_libs= + for deplib in $dependency_libs; do + case $deplib in + -L*) newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'`;; ### testsuite: skip nested quoting test + esac + # Need to link against all dependency_libs? + if test "$linkalldeplibs" = yes; then + deplibs="$deplib $deplibs" + else + # Need to hardcode shared library paths + # or/and link against static libraries + newdependency_libs="$deplib $newdependency_libs" + fi + if test "X$duplicate_deps" = "Xyes" ; then + case "$tmp_libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + tmp_libs="$tmp_libs $deplib" + done # for deplib + continue + fi # $linkmode = prog... + + if test "$linkmode,$pass" = "prog,link"; then + if test -n "$library_names" && + { test "$prefer_static_libs" = no || test -z "$old_library"; }; then + # We need to hardcode the library path + if test -n "$shlibpath_var"; then + # Make sure the rpath contains only unique directories. + case "$temp_rpath " in + *" $dir "*) ;; + *" $absdir "*) ;; + *) temp_rpath="$temp_rpath $dir" ;; + esac + fi + + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) compile_rpath="$compile_rpath $absdir" + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" + esac + ;; + esac + fi # $linkmode,$pass = prog,link... + + if test "$alldeplibs" = yes && + { test "$deplibs_check_method" = pass_all || + { test "$build_libtool_libs" = yes && + test -n "$library_names"; }; }; then + # We only need to search for static libraries + continue + fi + fi + + link_static=no # Whether the deplib will be linked statically + if test -n "$library_names" && + { test "$prefer_static_libs" = no || test -z "$old_library"; }; then + if test "$installed" = no; then + notinst_deplibs="$notinst_deplibs $lib" + need_relink=yes + fi + # This is a shared library + + # Warn about portability, can't link against -module's on + # some systems (darwin) + if test "$shouldnotlink" = yes && test "$pass" = link ; then + $echo + if test "$linkmode" = prog; then + $echo "*** Warning: Linking the executable $output against the loadable module" + else + $echo "*** Warning: Linking the shared library $output against the loadable module" + fi + $echo "*** $linklib is not portable!" + fi + if test "$linkmode" = lib && + test "$hardcode_into_libs" = yes; then + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) compile_rpath="$compile_rpath $absdir" + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" + esac + ;; + esac + fi + + if test -n "$old_archive_from_expsyms_cmds"; then + # figure out the soname + set dummy $library_names + realname="$2" + shift; shift + libname=`eval \\$echo \"$libname_spec\"` + # use dlname if we got it. it's perfectly good, no? + if test -n "$dlname"; then + soname="$dlname" + elif test -n "$soname_spec"; then + # bleh windows + case $host in + *cygwin* | mingw*) + major=`expr $current - $age` + versuffix="-$major" + ;; + esac + eval soname=\"$soname_spec\" + else + soname="$realname" + fi + + # Make a new name for the extract_expsyms_cmds to use + soroot="$soname" + soname=`$echo $soroot | ${SED} -e 's/^.*\///'` + newlib="libimp-`$echo $soname | ${SED} 's/^lib//;s/\.dll$//'`.a" + + # If the library has no export list, then create one now + if test -f "$output_objdir/$soname-def"; then : + else + $show "extracting exported symbol list from \`$soname'" + save_ifs="$IFS"; IFS='~' + cmds=$extract_expsyms_cmds + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + fi + + # Create $newlib + if test -f "$output_objdir/$newlib"; then :; else + $show "generating import library for \`$soname'" + save_ifs="$IFS"; IFS='~' + cmds=$old_archive_from_expsyms_cmds + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + fi + # make sure the library variables are pointing to the new library + dir=$output_objdir + linklib=$newlib + fi # test -n "$old_archive_from_expsyms_cmds" + + if test "$linkmode" = prog || test "$mode" != relink; then + add_shlibpath= + add_dir= + add= + lib_linked=yes + case $hardcode_action in + immediate | unsupported) + if test "$hardcode_direct" = no; then + add="$dir/$linklib" + case $host in + *-*-sco3.2v5* ) add_dir="-L$dir" ;; + *-*-darwin* ) + # if the lib is a module then we can not link against + # it, someone is ignoring the new warnings I added + if /usr/bin/file -L $add 2> /dev/null | $EGREP "bundle" >/dev/null ; then + $echo "** Warning, lib $linklib is a module, not a shared library" + if test -z "$old_library" ; then + $echo + $echo "** And there doesn't seem to be a static archive available" + $echo "** The link will probably fail, sorry" + else + add="$dir/$old_library" + fi + fi + esac + elif test "$hardcode_minus_L" = no; then + case $host in + *-*-sunos*) add_shlibpath="$dir" ;; + esac + add_dir="-L$dir" + add="-l$name" + elif test "$hardcode_shlibpath_var" = no; then + add_shlibpath="$dir" + add="-l$name" + else + lib_linked=no + fi + ;; + relink) + if test "$hardcode_direct" = yes; then + add="$dir/$linklib" + elif test "$hardcode_minus_L" = yes; then + add_dir="-L$dir" + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case "$libdir" in + [\\/]*) + add_dir="$add_dir -L$inst_prefix_dir$libdir" + ;; + esac + fi + add="-l$name" + elif test "$hardcode_shlibpath_var" = yes; then + add_shlibpath="$dir" + add="-l$name" + else + lib_linked=no + fi + ;; + *) lib_linked=no ;; + esac + + if test "$lib_linked" != yes; then + $echo "$modename: configuration error: unsupported hardcode properties" + exit $EXIT_FAILURE + fi + + if test -n "$add_shlibpath"; then + case :$compile_shlibpath: in + *":$add_shlibpath:"*) ;; + *) compile_shlibpath="$compile_shlibpath$add_shlibpath:" ;; + esac + fi + if test "$linkmode" = prog; then + test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" + test -n "$add" && compile_deplibs="$add $compile_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + if test "$hardcode_direct" != yes && \ + test "$hardcode_minus_L" != yes && \ + test "$hardcode_shlibpath_var" = yes; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; + esac + fi + fi + fi + + if test "$linkmode" = prog || test "$mode" = relink; then + add_shlibpath= + add_dir= + add= + # Finalize command for both is simple: just hardcode it. + if test "$hardcode_direct" = yes; then + add="$libdir/$linklib" + elif test "$hardcode_minus_L" = yes; then + add_dir="-L$libdir" + add="-l$name" + elif test "$hardcode_shlibpath_var" = yes; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; + esac + add="-l$name" + elif test "$hardcode_automatic" = yes; then + if test -n "$inst_prefix_dir" && + test -f "$inst_prefix_dir$libdir/$linklib" ; then + add="$inst_prefix_dir$libdir/$linklib" + else + add="$libdir/$linklib" + fi + else + # We cannot seem to hardcode it, guess we'll fake it. + add_dir="-L$libdir" + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case "$libdir" in + [\\/]*) + add_dir="$add_dir -L$inst_prefix_dir$libdir" + ;; + esac + fi + add="-l$name" + fi + + if test "$linkmode" = prog; then + test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" + test -n "$add" && finalize_deplibs="$add $finalize_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + fi + fi + elif test "$linkmode" = prog; then + # Here we assume that one of hardcode_direct or hardcode_minus_L + # is not unsupported. This is valid on all known static and + # shared platforms. + if test "$hardcode_direct" != unsupported; then + test -n "$old_library" && linklib="$old_library" + compile_deplibs="$dir/$linklib $compile_deplibs" + finalize_deplibs="$dir/$linklib $finalize_deplibs" + else + compile_deplibs="-l$name -L$dir $compile_deplibs" + finalize_deplibs="-l$name -L$dir $finalize_deplibs" + fi + elif test "$build_libtool_libs" = yes; then + # Not a shared library + if test "$deplibs_check_method" != pass_all; then + # We're trying link a shared library against a static one + # but the system doesn't support it. + + # Just print a warning and add the library to dependency_libs so + # that the program can be linked against the static library. + $echo + $echo "*** Warning: This system can not link to static lib archive $lib." + $echo "*** I have the capability to make that library automatically link in when" + $echo "*** you link to this library. But I can only do this if you have a" + $echo "*** shared version of the library, which you do not appear to have." + if test "$module" = yes; then + $echo "*** But as you try to build a module library, libtool will still create " + $echo "*** a static module, that should work as long as the dlopening application" + $echo "*** is linked with the -dlopen flag to resolve symbols at runtime." + if test -z "$global_symbol_pipe"; then + $echo + $echo "*** However, this would only work if libtool was able to extract symbol" + $echo "*** lists from a program, using \`nm' or equivalent, but libtool could" + $echo "*** not find such a program. So, this module is probably useless." + $echo "*** \`nm' from GNU binutils and a full rebuild may help." + fi + if test "$build_old_libs" = no; then + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + else + convenience="$convenience $dir/$old_library" + old_convenience="$old_convenience $dir/$old_library" + deplibs="$dir/$old_library $deplibs" + link_static=yes + fi + fi # link shared/static library? + + if test "$linkmode" = lib; then + if test -n "$dependency_libs" && + { test "$hardcode_into_libs" != yes || + test "$build_old_libs" = yes || + test "$link_static" = yes; }; then + # Extract -R from dependency_libs + temp_deplibs= + for libdir in $dependency_libs; do + case $libdir in + -R*) temp_xrpath=`$echo "X$libdir" | $Xsed -e 's/^-R//'` + case " $xrpath " in + *" $temp_xrpath "*) ;; + *) xrpath="$xrpath $temp_xrpath";; + esac;; + *) temp_deplibs="$temp_deplibs $libdir";; + esac + done + dependency_libs="$temp_deplibs" + fi + + newlib_search_path="$newlib_search_path $absdir" + # Link against this library + test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs" + # ... and its dependency_libs + tmp_libs= + for deplib in $dependency_libs; do + newdependency_libs="$deplib $newdependency_libs" + if test "X$duplicate_deps" = "Xyes" ; then + case "$tmp_libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + tmp_libs="$tmp_libs $deplib" + done + + if test "$link_all_deplibs" != no; then + # Add the search paths of all dependency libraries + for deplib in $dependency_libs; do + case $deplib in + -L*) path="$deplib" ;; + *.la) + dir=`$echo "X$deplib" | $Xsed -e 's%/[^/]*$%%'` + test "X$dir" = "X$deplib" && dir="." + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;; + *) + absdir=`cd "$dir" && pwd` + if test -z "$absdir"; then + $echo "$modename: warning: cannot determine absolute directory name of \`$dir'" 1>&2 + absdir="$dir" + fi + ;; + esac + if grep "^installed=no" $deplib > /dev/null; then + path="$absdir/$objdir" + else + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` + if test -z "$libdir"; then + $echo "$modename: \`$deplib' is not a valid libtool archive" 1>&2 + exit $EXIT_FAILURE + fi + if test "$absdir" != "$libdir"; then + $echo "$modename: warning: \`$deplib' seems to be moved" 1>&2 + fi + path="$absdir" + fi + depdepl= + case $host in + *-*-darwin*) + # we do not want to link against static libs, + # but need to link against shared + eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` + if test -n "$deplibrary_names" ; then + for tmp in $deplibrary_names ; do + depdepl=$tmp + done + if test -f "$path/$depdepl" ; then + depdepl="$path/$depdepl" + fi + # do not add paths which are already there + case " $newlib_search_path " in + *" $path "*) ;; + *) newlib_search_path="$newlib_search_path $path";; + esac + fi + path="" + ;; + *) + path="-L$path" + ;; + esac + ;; + -l*) + case $host in + *-*-darwin*) + # Again, we only want to link against shared libraries + eval tmp_libs=`$echo "X$deplib" | $Xsed -e "s,^\-l,,"` + for tmp in $newlib_search_path ; do + if test -f "$tmp/lib$tmp_libs.dylib" ; then + eval depdepl="$tmp/lib$tmp_libs.dylib" + break + fi + done + path="" + ;; + *) continue ;; + esac + ;; + *) continue ;; + esac + case " $deplibs " in + *" $depdepl "*) ;; + *) deplibs="$depdepl $deplibs" ;; + esac + case " $deplibs " in + *" $path "*) ;; + *) deplibs="$deplibs $path" ;; + esac + done + fi # link_all_deplibs != no + fi # linkmode = lib + done # for deplib in $libs + dependency_libs="$newdependency_libs" + if test "$pass" = dlpreopen; then + # Link the dlpreopened libraries before other libraries + for deplib in $save_deplibs; do + deplibs="$deplib $deplibs" + done + fi + if test "$pass" != dlopen; then + if test "$pass" != conv; then + # Make sure lib_search_path contains only unique directories. + lib_search_path= + for dir in $newlib_search_path; do + case "$lib_search_path " in + *" $dir "*) ;; + *) lib_search_path="$lib_search_path $dir" ;; + esac + done + newlib_search_path= + fi + + if test "$linkmode,$pass" != "prog,link"; then + vars="deplibs" + else + vars="compile_deplibs finalize_deplibs" + fi + for var in $vars dependency_libs; do + # Add libraries to $var in reverse order + eval tmp_libs=\"\$$var\" + new_libs= + for deplib in $tmp_libs; do + # FIXME: Pedantically, this is the right thing to do, so + # that some nasty dependency loop isn't accidentally + # broken: + #new_libs="$deplib $new_libs" + # Pragmatically, this seems to cause very few problems in + # practice: + case $deplib in + -L*) new_libs="$deplib $new_libs" ;; + -R*) ;; + *) + # And here is the reason: when a library appears more + # than once as an explicit dependence of a library, or + # is implicitly linked in more than once by the + # compiler, it is considered special, and multiple + # occurrences thereof are not removed. Compare this + # with having the same library being listed as a + # dependency of multiple other libraries: in this case, + # we know (pedantically, we assume) the library does not + # need to be listed more than once, so we keep only the + # last copy. This is not always right, but it is rare + # enough that we require users that really mean to play + # such unportable linking tricks to link the library + # using -Wl,-lname, so that libtool does not consider it + # for duplicate removal. + case " $specialdeplibs " in + *" $deplib "*) new_libs="$deplib $new_libs" ;; + *) + case " $new_libs " in + *" $deplib "*) ;; + *) new_libs="$deplib $new_libs" ;; + esac + ;; + esac + ;; + esac + done + tmp_libs= + for deplib in $new_libs; do + case $deplib in + -L*) + case " $tmp_libs " in + *" $deplib "*) ;; + *) tmp_libs="$tmp_libs $deplib" ;; + esac + ;; + *) tmp_libs="$tmp_libs $deplib" ;; + esac + done + eval $var=\"$tmp_libs\" + done # for var + fi + # Last step: remove runtime libs from dependency_libs + # (they stay in deplibs) + tmp_libs= + for i in $dependency_libs ; do + case " $predeps $postdeps $compiler_lib_search_path " in + *" $i "*) + i="" + ;; + esac + if test -n "$i" ; then + tmp_libs="$tmp_libs $i" + fi + done + dependency_libs=$tmp_libs + done # for pass + if test "$linkmode" = prog; then + dlfiles="$newdlfiles" + dlprefiles="$newdlprefiles" + fi + + case $linkmode in + oldlib) + if test -n "$deplibs"; then + $echo "$modename: warning: \`-l' and \`-L' are ignored for archives" 1>&2 + fi + + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + $echo "$modename: warning: \`-dlopen' is ignored for archives" 1>&2 + fi + + if test -n "$rpath"; then + $echo "$modename: warning: \`-rpath' is ignored for archives" 1>&2 + fi + + if test -n "$xrpath"; then + $echo "$modename: warning: \`-R' is ignored for archives" 1>&2 + fi + + if test -n "$vinfo"; then + $echo "$modename: warning: \`-version-info/-version-number' is ignored for archives" 1>&2 + fi + + if test -n "$release"; then + $echo "$modename: warning: \`-release' is ignored for archives" 1>&2 + fi + + if test -n "$export_symbols" || test -n "$export_symbols_regex"; then + $echo "$modename: warning: \`-export-symbols' is ignored for archives" 1>&2 + fi + + # Now set the variables for building old libraries. + build_libtool_libs=no + oldlibs="$output" + objs="$objs$old_deplibs" + ;; + + lib) + # Make sure we only generate libraries of the form `libNAME.la'. + case $outputname in + lib*) + name=`$echo "X$outputname" | $Xsed -e 's/\.la$//' -e 's/^lib//'` + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + ;; + *) + if test "$module" = no; then + $echo "$modename: libtool library \`$output' must begin with \`lib'" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + if test "$need_lib_prefix" != no; then + # Add the "lib" prefix for modules if required + name=`$echo "X$outputname" | $Xsed -e 's/\.la$//'` + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + else + libname=`$echo "X$outputname" | $Xsed -e 's/\.la$//'` + fi + ;; + esac + + if test -n "$objs"; then + if test "$deplibs_check_method" != pass_all; then + $echo "$modename: cannot build libtool library \`$output' from non-libtool objects on this host:$objs" 2>&1 + exit $EXIT_FAILURE + else + $echo + $echo "*** Warning: Linking the shared library $output against the non-libtool" + $echo "*** objects $objs is not portable!" + libobjs="$libobjs $objs" + fi + fi + + if test "$dlself" != no; then + $echo "$modename: warning: \`-dlopen self' is ignored for libtool libraries" 1>&2 + fi + + set dummy $rpath + if test "$#" -gt 2; then + $echo "$modename: warning: ignoring multiple \`-rpath's for a libtool library" 1>&2 + fi + install_libdir="$2" + + oldlibs= + if test -z "$rpath"; then + if test "$build_libtool_libs" = yes; then + # Building a libtool convenience library. + # Some compilers have problems with a `.al' extension so + # convenience libraries should have the same extension an + # archive normally would. + oldlibs="$output_objdir/$libname.$libext $oldlibs" + build_libtool_libs=convenience + build_old_libs=yes + fi + + if test -n "$vinfo"; then + $echo "$modename: warning: \`-version-info/-version-number' is ignored for convenience libraries" 1>&2 + fi + + if test -n "$release"; then + $echo "$modename: warning: \`-release' is ignored for convenience libraries" 1>&2 + fi + else + + # Parse the version information argument. + save_ifs="$IFS"; IFS=':' + set dummy $vinfo 0 0 0 + IFS="$save_ifs" + + if test -n "$8"; then + $echo "$modename: too many parameters to \`-version-info'" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + # convert absolute version numbers to libtool ages + # this retains compatibility with .la files and attempts + # to make the code below a bit more comprehensible + + case $vinfo_number in + yes) + number_major="$2" + number_minor="$3" + number_revision="$4" + # + # There are really only two kinds -- those that + # use the current revision as the major version + # and those that subtract age and use age as + # a minor version. But, then there is irix + # which has an extra 1 added just for fun + # + case $version_type in + darwin|linux|osf|windows) + current=`expr $number_major + $number_minor` + age="$number_minor" + revision="$number_revision" + ;; + freebsd-aout|freebsd-elf|sunos) + current="$number_major" + revision="$number_minor" + age="0" + ;; + irix|nonstopux) + current=`expr $number_major + $number_minor - 1` + age="$number_minor" + revision="$number_minor" + ;; + esac + ;; + no) + current="$2" + revision="$3" + age="$4" + ;; + esac + + # Check that each of the things are valid numbers. + case $current in + 0 | [1-9] | [1-9][0-9] | [1-9][0-9][0-9]) ;; + *) + $echo "$modename: CURRENT \`$current' is not a nonnegative integer" 1>&2 + $echo "$modename: \`$vinfo' is not valid version information" 1>&2 + exit $EXIT_FAILURE + ;; + esac + + case $revision in + 0 | [1-9] | [1-9][0-9] | [1-9][0-9][0-9]) ;; + *) + $echo "$modename: REVISION \`$revision' is not a nonnegative integer" 1>&2 + $echo "$modename: \`$vinfo' is not valid version information" 1>&2 + exit $EXIT_FAILURE + ;; + esac + + case $age in + 0 | [1-9] | [1-9][0-9] | [1-9][0-9][0-9]) ;; + *) + $echo "$modename: AGE \`$age' is not a nonnegative integer" 1>&2 + $echo "$modename: \`$vinfo' is not valid version information" 1>&2 + exit $EXIT_FAILURE + ;; + esac + + if test "$age" -gt "$current"; then + $echo "$modename: AGE \`$age' is greater than the current interface number \`$current'" 1>&2 + $echo "$modename: \`$vinfo' is not valid version information" 1>&2 + exit $EXIT_FAILURE + fi + + # Calculate the version variables. + major= + versuffix= + verstring= + case $version_type in + none) ;; + + darwin) + # Like Linux, but with the current version available in + # verstring for coding it into the library header + major=.`expr $current - $age` + versuffix="$major.$age.$revision" + # Darwin ld doesn't like 0 for these options... + minor_current=`expr $current + 1` + verstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision" + ;; + + freebsd-aout) + major=".$current" + versuffix=".$current.$revision"; + ;; + + freebsd-elf) + major=".$current" + versuffix=".$current"; + ;; + + irix | nonstopux) + major=`expr $current - $age + 1` + + case $version_type in + nonstopux) verstring_prefix=nonstopux ;; + *) verstring_prefix=sgi ;; + esac + verstring="$verstring_prefix$major.$revision" + + # Add in all the interfaces that we are compatible with. + loop=$revision + while test "$loop" -ne 0; do + iface=`expr $revision - $loop` + loop=`expr $loop - 1` + verstring="$verstring_prefix$major.$iface:$verstring" + done + + # Before this point, $major must not contain `.'. + major=.$major + versuffix="$major.$revision" + ;; + + linux) + major=.`expr $current - $age` + versuffix="$major.$age.$revision" + ;; + + osf) + major=.`expr $current - $age` + versuffix=".$current.$age.$revision" + verstring="$current.$age.$revision" + + # Add in all the interfaces that we are compatible with. + loop=$age + while test "$loop" -ne 0; do + iface=`expr $current - $loop` + loop=`expr $loop - 1` + verstring="$verstring:${iface}.0" + done + + # Make executables depend on our current version. + verstring="$verstring:${current}.0" + ;; + + sunos) + major=".$current" + versuffix=".$current.$revision" + ;; + + windows) + # Use '-' rather than '.', since we only want one + # extension on DOS 8.3 filesystems. + major=`expr $current - $age` + versuffix="-$major" + ;; + + *) + $echo "$modename: unknown library version type \`$version_type'" 1>&2 + $echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2 + exit $EXIT_FAILURE + ;; + esac + + # Clear the version info if we defaulted, and they specified a release. + if test -z "$vinfo" && test -n "$release"; then + major= + case $version_type in + darwin) + # we can't check for "0.0" in archive_cmds due to quoting + # problems, so we reset it completely + verstring= + ;; + *) + verstring="0.0" + ;; + esac + if test "$need_version" = no; then + versuffix= + else + versuffix=".0.0" + fi + fi + + # Remove version info from name if versioning should be avoided + if test "$avoid_version" = yes && test "$need_version" = no; then + major= + versuffix= + verstring="" + fi + + # Check to see if the archive will have undefined symbols. + if test "$allow_undefined" = yes; then + if test "$allow_undefined_flag" = unsupported; then + $echo "$modename: warning: undefined symbols not allowed in $host shared libraries" 1>&2 + build_libtool_libs=no + build_old_libs=yes + fi + else + # Don't allow undefined symbols. + allow_undefined_flag="$no_undefined_flag" + fi + fi + + if test "$mode" != relink; then + # Remove our outputs, but don't remove object files since they + # may have been created when compiling PIC objects. + removelist= + tempremovelist=`$echo "$output_objdir/*"` + for p in $tempremovelist; do + case $p in + *.$objext) + ;; + $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*) + if test "X$precious_files_regex" != "X"; then + if echo $p | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 + then + continue + fi + fi + removelist="$removelist $p" + ;; + *) ;; + esac + done + if test -n "$removelist"; then + $show "${rm}r $removelist" + $run ${rm}r $removelist + fi + fi + + # Now set the variables for building old libraries. + if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then + oldlibs="$oldlibs $output_objdir/$libname.$libext" + + # Transform .lo files to .o files. + oldobjs="$objs "`$echo "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}'$/d' -e "$lo2o" | $NL2SP` + fi + + # Eliminate all temporary directories. + for path in $notinst_path; do + lib_search_path=`$echo "$lib_search_path " | ${SED} -e 's% $path % %g'` + deplibs=`$echo "$deplibs " | ${SED} -e 's% -L$path % %g'` + dependency_libs=`$echo "$dependency_libs " | ${SED} -e 's% -L$path % %g'` + done + + if test -n "$xrpath"; then + # If the user specified any rpath flags, then add them. + temp_xrpath= + for libdir in $xrpath; do + temp_xrpath="$temp_xrpath -R$libdir" + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" ;; + esac + done + if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then + dependency_libs="$temp_xrpath $dependency_libs" + fi + fi + + # Make sure dlfiles contains only unique files that won't be dlpreopened + old_dlfiles="$dlfiles" + dlfiles= + for lib in $old_dlfiles; do + case " $dlprefiles $dlfiles " in + *" $lib "*) ;; + *) dlfiles="$dlfiles $lib" ;; + esac + done + + # Make sure dlprefiles contains only unique files + old_dlprefiles="$dlprefiles" + dlprefiles= + for lib in $old_dlprefiles; do + case "$dlprefiles " in + *" $lib "*) ;; + *) dlprefiles="$dlprefiles $lib" ;; + esac + done + + if test "$build_libtool_libs" = yes; then + if test -n "$rpath"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos*) + # these systems don't actually have a c library (as such)! + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C library is in the System framework + deplibs="$deplibs -framework System" + ;; + *-*-netbsd*) + # Don't link with libc until the a.out ld.so is fixed. + ;; + *-*-openbsd* | *-*-freebsd*) + # Do not include libc due to us having libc/libc_r. + test "X$arg" = "X-lc" && continue + ;; + *) + # Add libc to deplibs on all other systems if necessary. + if test "$build_libtool_need_lc" = "yes"; then + deplibs="$deplibs -lc" + fi + ;; + esac + fi + + # Transform deplibs into only deplibs that can be linked in shared. + name_save=$name + libname_save=$libname + release_save=$release + versuffix_save=$versuffix + major_save=$major + # I'm not sure if I'm treating the release correctly. I think + # release should show up in the -l (ie -lgmp5) so we don't want to + # add it in twice. Is that correct? + release="" + versuffix="" + major="" + newdeplibs= + droppeddeps=no + case $deplibs_check_method in + pass_all) + # Don't check for shared/static. Everything works. + # This might be a little naive. We might want to check + # whether the library exists or not. But this is on + # osf3 & osf4 and I'm not really sure... Just + # implementing what was already the behavior. + newdeplibs=$deplibs + ;; + test_compile) + # This code stresses the "libraries are programs" paradigm to its + # limits. Maybe even breaks it. We compile a program, linking it + # against the deplibs as a proxy for the library. Then we can check + # whether they linked in statically or dynamically with ldd. + $rm conftest.c + cat > conftest.c </dev/null` + for potent_lib in $potential_libs; do + # Follow soft links. + if ls -lLd "$potent_lib" 2>/dev/null \ + | grep " -> " >/dev/null; then + continue + fi + # The statement above tries to avoid entering an + # endless loop below, in case of cyclic links. + # We might still enter an endless loop, since a link + # loop can be closed while we follow links, + # but so what? + potlib="$potent_lib" + while test -h "$potlib" 2>/dev/null; do + potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'` + case $potliblink in + [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";; + *) potlib=`$echo "X$potlib" | $Xsed -e 's,[^/]*$,,'`"$potliblink";; + esac + done + if eval $file_magic_cmd \"\$potlib\" 2>/dev/null \ + | ${SED} 10q \ + | $EGREP "$file_magic_regex" > /dev/null; then + newdeplibs="$newdeplibs $a_deplib" + a_deplib="" + break 2 + fi + done + done + fi + if test -n "$a_deplib" ; then + droppeddeps=yes + $echo + $echo "*** Warning: linker path does not have real file for library $a_deplib." + $echo "*** I have the capability to make that library automatically link in when" + $echo "*** you link to this library. But I can only do this if you have a" + $echo "*** shared version of the library, which you do not appear to have" + $echo "*** because I did check the linker path looking for a file starting" + if test -z "$potlib" ; then + $echo "*** with $libname but no candidates were found. (...for file magic test)" + else + $echo "*** with $libname and none of the candidates passed a file format test" + $echo "*** using a file magic. Last file checked: $potlib" + fi + fi + else + # Add a -L argument. + newdeplibs="$newdeplibs $a_deplib" + fi + done # Gone through all deplibs. + ;; + match_pattern*) + set dummy $deplibs_check_method + match_pattern_regex=`expr "$deplibs_check_method" : "$2 \(.*\)"` + for a_deplib in $deplibs; do + name="`expr $a_deplib : '-l\(.*\)'`" + # If $name is empty we are operating on a -L argument. + if test -n "$name" && test "$name" != "0"; then + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $a_deplib "*) + newdeplibs="$newdeplibs $a_deplib" + a_deplib="" + ;; + esac + fi + if test -n "$a_deplib" ; then + libname=`eval \\$echo \"$libname_spec\"` + for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do + potential_libs=`ls $i/$libname[.-]* 2>/dev/null` + for potent_lib in $potential_libs; do + potlib="$potent_lib" # see symlink-check above in file_magic test + if eval $echo \"$potent_lib\" 2>/dev/null \ + | ${SED} 10q \ + | $EGREP "$match_pattern_regex" > /dev/null; then + newdeplibs="$newdeplibs $a_deplib" + a_deplib="" + break 2 + fi + done + done + fi + if test -n "$a_deplib" ; then + droppeddeps=yes + $echo + $echo "*** Warning: linker path does not have real file for library $a_deplib." + $echo "*** I have the capability to make that library automatically link in when" + $echo "*** you link to this library. But I can only do this if you have a" + $echo "*** shared version of the library, which you do not appear to have" + $echo "*** because I did check the linker path looking for a file starting" + if test -z "$potlib" ; then + $echo "*** with $libname but no candidates were found. (...for regex pattern test)" + else + $echo "*** with $libname and none of the candidates passed a file format test" + $echo "*** using a regex pattern. Last file checked: $potlib" + fi + fi + else + # Add a -L argument. + newdeplibs="$newdeplibs $a_deplib" + fi + done # Gone through all deplibs. + ;; + none | unknown | *) + newdeplibs="" + tmp_deplibs=`$echo "X $deplibs" | $Xsed -e 's/ -lc$//' \ + -e 's/ -[LR][^ ]*//g'` + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + for i in $predeps $postdeps ; do + # can't use Xsed below, because $i might contain '/' + tmp_deplibs=`$echo "X $tmp_deplibs" | ${SED} -e "1s,^X,," -e "s,$i,,"` + done + fi + if $echo "X $tmp_deplibs" | $Xsed -e 's/[ ]//g' \ + | grep . >/dev/null; then + $echo + if test "X$deplibs_check_method" = "Xnone"; then + $echo "*** Warning: inter-library dependencies are not supported in this platform." + else + $echo "*** Warning: inter-library dependencies are not known to be supported." + fi + $echo "*** All declared inter-library dependencies are being dropped." + droppeddeps=yes + fi + ;; + esac + versuffix=$versuffix_save + major=$major_save + release=$release_save + libname=$libname_save + name=$name_save + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library is the System framework + newdeplibs=`$echo "X $newdeplibs" | $Xsed -e 's/ -lc / -framework System /'` + ;; + esac + + if test "$droppeddeps" = yes; then + if test "$module" = yes; then + $echo + $echo "*** Warning: libtool could not satisfy all declared inter-library" + $echo "*** dependencies of module $libname. Therefore, libtool will create" + $echo "*** a static module, that should work as long as the dlopening" + $echo "*** application is linked with the -dlopen flag." + if test -z "$global_symbol_pipe"; then + $echo + $echo "*** However, this would only work if libtool was able to extract symbol" + $echo "*** lists from a program, using \`nm' or equivalent, but libtool could" + $echo "*** not find such a program. So, this module is probably useless." + $echo "*** \`nm' from GNU binutils and a full rebuild may help." + fi + if test "$build_old_libs" = no; then + oldlibs="$output_objdir/$libname.$libext" + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + else + $echo "*** The inter-library dependencies that have been dropped here will be" + $echo "*** automatically added whenever a program is linked with this library" + $echo "*** or is declared to -dlopen it." + + if test "$allow_undefined" = no; then + $echo + $echo "*** Since this library must not contain undefined symbols," + $echo "*** because either the platform does not support them or" + $echo "*** it was explicitly requested with -no-undefined," + $echo "*** libtool will only create a static version of it." + if test "$build_old_libs" = no; then + oldlibs="$output_objdir/$libname.$libext" + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + fi + fi + # Done checking deplibs! + deplibs=$newdeplibs + fi + + # All the library-specific variables (install_libdir is set above). + library_names= + old_library= + dlname= + + # Test again, we may have decided not to build it any more + if test "$build_libtool_libs" = yes; then + if test "$hardcode_into_libs" = yes; then + # Hardcode the library paths + hardcode_libdirs= + dep_rpath= + rpath="$finalize_rpath" + test "$mode" != relink && rpath="$compile_rpath$rpath" + for libdir in $rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + dep_rpath="$dep_rpath $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) perm_rpath="$perm_rpath $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + if test -n "$hardcode_libdir_flag_spec_ld"; then + eval dep_rpath=\"$hardcode_libdir_flag_spec_ld\" + else + eval dep_rpath=\"$hardcode_libdir_flag_spec\" + fi + fi + if test -n "$runpath_var" && test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + rpath="$rpath$dir:" + done + eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" + fi + test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" + fi + + shlibpath="$finalize_shlibpath" + test "$mode" != relink && shlibpath="$compile_shlibpath$shlibpath" + if test -n "$shlibpath"; then + eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" + fi + + # Get the real and link names of the library. + eval shared_ext=\"$shrext_cmds\" + eval library_names=\"$library_names_spec\" + set dummy $library_names + realname="$2" + shift; shift + + if test -n "$soname_spec"; then + eval soname=\"$soname_spec\" + else + soname="$realname" + fi + if test -z "$dlname"; then + dlname=$soname + fi + + lib="$output_objdir/$realname" + for link + do + linknames="$linknames $link" + done + + # Use standard objects if they are pic + test -z "$pic_flag" && libobjs=`$echo "X$libobjs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then + $show "generating symbol list for \`$libname.la'" + export_symbols="$output_objdir/$libname.exp" + $run $rm $export_symbols + cmds=$export_symbols_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + if len=`expr "X$cmd" : ".*"` && + test "$len" -le "$max_cmd_len" || test "$max_cmd_len" -le -1; then + $show "$cmd" + $run eval "$cmd" || exit $? + skipped_export=false + else + # The command line is too long to execute in one step. + $show "using reloadable object file for export list..." + skipped_export=: + fi + done + IFS="$save_ifs" + if test -n "$export_symbols_regex"; then + $show "$EGREP -e \"$export_symbols_regex\" \"$export_symbols\" > \"${export_symbols}T\"" + $run eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + $show "$mv \"${export_symbols}T\" \"$export_symbols\"" + $run eval '$mv "${export_symbols}T" "$export_symbols"' + fi + fi + fi + + if test -n "$export_symbols" && test -n "$include_expsyms"; then + $run eval '$echo "X$include_expsyms" | $SP2NL >> "$export_symbols"' + fi + + tmp_deplibs= + for test_deplib in $deplibs; do + case " $convenience " in + *" $test_deplib "*) ;; + *) + tmp_deplibs="$tmp_deplibs $test_deplib" + ;; + esac + done + deplibs="$tmp_deplibs" + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + else + gentop="$output_objdir/${outputname}x" + generated="$generated $gentop" + + func_extract_archives $gentop $convenience + libobjs="$libobjs $func_extract_archives_result" + fi + fi + + if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then + eval flag=\"$thread_safe_flag_spec\" + linker_flags="$linker_flags $flag" + fi + + # Make a backup of the uninstalled library when relinking + if test "$mode" = relink; then + $run eval '(cd $output_objdir && $rm ${realname}U && $mv $realname ${realname}U)' || exit $? + fi + + # Do each of the archive commands. + if test "$module" = yes && test -n "$module_cmds" ; then + if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then + eval test_cmds=\"$module_expsym_cmds\" + cmds=$module_expsym_cmds + else + eval test_cmds=\"$module_cmds\" + cmds=$module_cmds + fi + else + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + eval test_cmds=\"$archive_expsym_cmds\" + cmds=$archive_expsym_cmds + else + eval test_cmds=\"$archive_cmds\" + cmds=$archive_cmds + fi + fi + + if test "X$skipped_export" != "X:" && len=`expr "X$test_cmds" : ".*"` && + test "$len" -le "$max_cmd_len" || test "$max_cmd_len" -le -1; then + : + else + # The command line is too long to link in one step, link piecewise. + $echo "creating reloadable object files..." + + # Save the value of $output and $libobjs because we want to + # use them later. If we have whole_archive_flag_spec, we + # want to use save_libobjs as it was before + # whole_archive_flag_spec was expanded, because we can't + # assume the linker understands whole_archive_flag_spec. + # This may have to be revisited, in case too many + # convenience libraries get linked in and end up exceeding + # the spec. + if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + fi + save_output=$output + + # Clear the reloadable object creation command queue and + # initialize k to one. + test_cmds= + concat_cmds= + objlist= + delfiles= + last_robj= + k=1 + output=$output_objdir/$save_output-${k}.$objext + # Loop over the list of objects to be linked. + for obj in $save_libobjs + do + eval test_cmds=\"$reload_cmds $objlist $last_robj\" + if test "X$objlist" = X || + { len=`expr "X$test_cmds" : ".*"` && + test "$len" -le "$max_cmd_len"; }; then + objlist="$objlist $obj" + else + # The command $test_cmds is almost too long, add a + # command to the queue. + if test "$k" -eq 1 ; then + # The first file doesn't have a previous command to add. + eval concat_cmds=\"$reload_cmds $objlist $last_robj\" + else + # All subsequent reloadable object files will link in + # the last one created. + eval concat_cmds=\"\$concat_cmds~$reload_cmds $objlist $last_robj\" + fi + last_robj=$output_objdir/$save_output-${k}.$objext + k=`expr $k + 1` + output=$output_objdir/$save_output-${k}.$objext + objlist=$obj + len=1 + fi + done + # Handle the remaining objects by creating one last + # reloadable object file. All subsequent reloadable object + # files will link in the last one created. + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\${concat_cmds}$reload_cmds $objlist $last_robj\" + + if ${skipped_export-false}; then + $show "generating symbol list for \`$libname.la'" + export_symbols="$output_objdir/$libname.exp" + $run $rm $export_symbols + libobjs=$output + # Append the command to create the export file. + eval concat_cmds=\"\$concat_cmds~$export_symbols_cmds\" + fi + + # Set up a command to remove the reloadale object files + # after they are used. + i=0 + while test "$i" -lt "$k" + do + i=`expr $i + 1` + delfiles="$delfiles $output_objdir/$save_output-${i}.$objext" + done + + $echo "creating a temporary reloadable object file: $output" + + # Loop through the commands generated above and execute them. + save_ifs="$IFS"; IFS='~' + for cmd in $concat_cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + + libobjs=$output + # Restore the value of output. + output=$save_output + + if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + fi + # Expand the library linking commands again to reset the + # value of $libobjs for piecewise linking. + + # Do each of the archive commands. + if test "$module" = yes && test -n "$module_cmds" ; then + if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then + cmds=$module_expsym_cmds + else + cmds=$module_cmds + fi + else + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + cmds=$archive_expsym_cmds + else + cmds=$archive_cmds + fi + fi + + # Append the command to remove the reloadable object files + # to the just-reset $cmds. + eval cmds=\"\$cmds~\$rm $delfiles\" + fi + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + + # Restore the uninstalled library and exit + if test "$mode" = relink; then + $run eval '(cd $output_objdir && $rm ${realname}T && $mv $realname ${realname}T && $mv "$realname"U $realname)' || exit $? + exit $EXIT_SUCCESS + fi + + # Create links to the real library. + for linkname in $linknames; do + if test "$realname" != "$linkname"; then + $show "(cd $output_objdir && $rm $linkname && $LN_S $realname $linkname)" + $run eval '(cd $output_objdir && $rm $linkname && $LN_S $realname $linkname)' || exit $? + fi + done + + # If -module or -export-dynamic was specified, set the dlname. + if test "$module" = yes || test "$export_dynamic" = yes; then + # On all known operating systems, these are identical. + dlname="$soname" + fi + fi + ;; + + obj) + if test -n "$deplibs"; then + $echo "$modename: warning: \`-l' and \`-L' are ignored for objects" 1>&2 + fi + + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + $echo "$modename: warning: \`-dlopen' is ignored for objects" 1>&2 + fi + + if test -n "$rpath"; then + $echo "$modename: warning: \`-rpath' is ignored for objects" 1>&2 + fi + + if test -n "$xrpath"; then + $echo "$modename: warning: \`-R' is ignored for objects" 1>&2 + fi + + if test -n "$vinfo"; then + $echo "$modename: warning: \`-version-info' is ignored for objects" 1>&2 + fi + + if test -n "$release"; then + $echo "$modename: warning: \`-release' is ignored for objects" 1>&2 + fi + + case $output in + *.lo) + if test -n "$objs$old_deplibs"; then + $echo "$modename: cannot build library object \`$output' from non-libtool objects" 1>&2 + exit $EXIT_FAILURE + fi + libobj="$output" + obj=`$echo "X$output" | $Xsed -e "$lo2o"` + ;; + *) + libobj= + obj="$output" + ;; + esac + + # Delete the old objects. + $run $rm $obj $libobj + + # Objects from convenience libraries. This assumes + # single-version convenience libraries. Whenever we create + # different ones for PIC/non-PIC, this we'll have to duplicate + # the extraction. + reload_conv_objs= + gentop= + # reload_cmds runs $LD directly, so let us get rid of + # -Wl from whole_archive_flag_spec + wl= + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec"; then + eval reload_conv_objs=\"\$reload_objs $whole_archive_flag_spec\" + else + gentop="$output_objdir/${obj}x" + generated="$generated $gentop" + + func_extract_archives $gentop $convenience + reload_conv_objs="$reload_objs $func_extract_archives_result" + fi + fi + + # Create the old-style object. + reload_objs="$objs$old_deplibs "`$echo "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}$'/d' -e '/\.lib$/d' -e "$lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test + + output="$obj" + cmds=$reload_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + + # Exit if we aren't doing a library object file. + if test -z "$libobj"; then + if test -n "$gentop"; then + $show "${rm}r $gentop" + $run ${rm}r $gentop + fi + + exit $EXIT_SUCCESS + fi + + if test "$build_libtool_libs" != yes; then + if test -n "$gentop"; then + $show "${rm}r $gentop" + $run ${rm}r $gentop + fi + + # Create an invalid libtool object if no PIC, so that we don't + # accidentally link it into a program. + # $show "echo timestamp > $libobj" + # $run eval "echo timestamp > $libobj" || exit $? + exit $EXIT_SUCCESS + fi + + if test -n "$pic_flag" || test "$pic_mode" != default; then + # Only do commands if we really have different PIC objects. + reload_objs="$libobjs $reload_conv_objs" + output="$libobj" + cmds=$reload_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + fi + + if test -n "$gentop"; then + $show "${rm}r $gentop" + $run ${rm}r $gentop + fi + + exit $EXIT_SUCCESS + ;; + + prog) + case $host in + *cygwin*) output=`$echo $output | ${SED} -e 's,.exe$,,;s,$,.exe,'` ;; + esac + if test -n "$vinfo"; then + $echo "$modename: warning: \`-version-info' is ignored for programs" 1>&2 + fi + + if test -n "$release"; then + $echo "$modename: warning: \`-release' is ignored for programs" 1>&2 + fi + + if test "$preload" = yes; then + if test "$dlopen_support" = unknown && test "$dlopen_self" = unknown && + test "$dlopen_self_static" = unknown; then + $echo "$modename: warning: \`AC_LIBTOOL_DLOPEN' not used. Assuming no dlopen support." + fi + fi + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library is the System framework + compile_deplibs=`$echo "X $compile_deplibs" | $Xsed -e 's/ -lc / -framework System /'` + finalize_deplibs=`$echo "X $finalize_deplibs" | $Xsed -e 's/ -lc / -framework System /'` + ;; + esac + + case $host in + *darwin*) + # Don't allow lazy linking, it breaks C++ global constructors + if test "$tagname" = CXX ; then + compile_command="$compile_command ${wl}-bind_at_load" + finalize_command="$finalize_command ${wl}-bind_at_load" + fi + ;; + esac + + compile_command="$compile_command $compile_deplibs" + finalize_command="$finalize_command $finalize_deplibs" + + if test -n "$rpath$xrpath"; then + # If the user specified any rpath flags, then add them. + for libdir in $rpath $xrpath; do + # This is the magic to use -rpath. + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" ;; + esac + done + fi + + # Now hardcode the library paths + rpath= + hardcode_libdirs= + for libdir in $compile_rpath $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + rpath="$rpath $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) perm_rpath="$perm_rpath $libdir" ;; + esac + fi + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) + case :$dllsearchpath: in + *":$libdir:"*) ;; + *) dllsearchpath="$dllsearchpath:$libdir";; + esac + ;; + esac + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + compile_rpath="$rpath" + + rpath= + hardcode_libdirs= + for libdir in $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + rpath="$rpath $flag" + fi + elif test -n "$runpath_var"; then + case "$finalize_perm_rpath " in + *" $libdir "*) ;; + *) finalize_perm_rpath="$finalize_perm_rpath $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + finalize_rpath="$rpath" + + if test -n "$libobjs" && test "$build_old_libs" = yes; then + # Transform all the library objects into standard objects. + compile_command=`$echo "X$compile_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + finalize_command=`$echo "X$finalize_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + fi + + dlsyms= + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + if test -n "$NM" && test -n "$global_symbol_pipe"; then + dlsyms="${outputname}S.c" + else + $echo "$modename: not configured to extract global symbols from dlpreopened files" 1>&2 + fi + fi + + if test -n "$dlsyms"; then + case $dlsyms in + "") ;; + *.c) + # Discover the nlist of each of the dlfiles. + nlist="$output_objdir/${outputname}.nm" + + $show "$rm $nlist ${nlist}S ${nlist}T" + $run $rm "$nlist" "${nlist}S" "${nlist}T" + + # Parse the name list into a source file. + $show "creating $output_objdir/$dlsyms" + + test -z "$run" && $echo > "$output_objdir/$dlsyms" "\ +/* $dlsyms - symbol resolution table for \`$outputname' dlsym emulation. */ +/* Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP */ + +#ifdef __cplusplus +extern \"C\" { +#endif + +/* Prevent the only kind of declaration conflicts we can make. */ +#define lt_preloaded_symbols some_other_symbol + +/* External symbol declarations for the compiler. */\ +" + + if test "$dlself" = yes; then + $show "generating symbol list for \`$output'" + + test -z "$run" && $echo ': @PROGRAM@ ' > "$nlist" + + # Add our own program objects to the symbol list. + progfiles=`$echo "X$objs$old_deplibs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + for arg in $progfiles; do + $show "extracting global C symbols from \`$arg'" + $run eval "$NM $arg | $global_symbol_pipe >> '$nlist'" + done + + if test -n "$exclude_expsyms"; then + $run eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' + $run eval '$mv "$nlist"T "$nlist"' + fi + + if test -n "$export_symbols_regex"; then + $run eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' + $run eval '$mv "$nlist"T "$nlist"' + fi + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + export_symbols="$output_objdir/$output.exp" + $run $rm $export_symbols + $run eval "${SED} -n -e '/^: @PROGRAM@$/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' + else + $run eval "${SED} -e 's/\([][.*^$]\)/\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$output.exp"' + $run eval 'grep -f "$output_objdir/$output.exp" < "$nlist" > "$nlist"T' + $run eval 'mv "$nlist"T "$nlist"' + fi + fi + + for arg in $dlprefiles; do + $show "extracting global C symbols from \`$arg'" + name=`$echo "$arg" | ${SED} -e 's%^.*/%%'` + $run eval '$echo ": $name " >> "$nlist"' + $run eval "$NM $arg | $global_symbol_pipe >> '$nlist'" + done + + if test -z "$run"; then + # Make sure we have at least an empty file. + test -f "$nlist" || : > "$nlist" + + if test -n "$exclude_expsyms"; then + $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T + $mv "$nlist"T "$nlist" + fi + + # Try sorting and uniquifying the output. + if grep -v "^: " < "$nlist" | + if sort -k 3 /dev/null 2>&1; then + sort -k 3 + else + sort +2 + fi | + uniq > "$nlist"S; then + : + else + grep -v "^: " < "$nlist" > "$nlist"S + fi + + if test -f "$nlist"S; then + eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$dlsyms"' + else + $echo '/* NONE */' >> "$output_objdir/$dlsyms" + fi + + $echo >> "$output_objdir/$dlsyms" "\ + +#undef lt_preloaded_symbols + +#if defined (__STDC__) && __STDC__ +# define lt_ptr void * +#else +# define lt_ptr char * +# define const +#endif + +/* The mapping between symbol names and symbols. */ +const struct { + const char *name; + lt_ptr address; +} +lt_preloaded_symbols[] = +{\ +" + + eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$dlsyms" + + $echo >> "$output_objdir/$dlsyms" "\ + {0, (lt_ptr) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif\ +" + fi + + pic_flag_for_symtable= + case $host in + # compiling the symbol table file with pic_flag works around + # a FreeBSD bug that causes programs to crash when -lm is + # linked before any other PIC object. But we must not use + # pic_flag when linking with -static. The problem exists in + # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. + *-*-freebsd2*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) + case "$compile_command " in + *" -static "*) ;; + *) pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND";; + esac;; + *-*-hpux*) + case "$compile_command " in + *" -static "*) ;; + *) pic_flag_for_symtable=" $pic_flag";; + esac + esac + + # Now compile the dynamic symbol file. + $show "(cd $output_objdir && $LTCC -c$no_builtin_flag$pic_flag_for_symtable \"$dlsyms\")" + $run eval '(cd $output_objdir && $LTCC -c$no_builtin_flag$pic_flag_for_symtable "$dlsyms")' || exit $? + + # Clean up the generated files. + $show "$rm $output_objdir/$dlsyms $nlist ${nlist}S ${nlist}T" + $run $rm "$output_objdir/$dlsyms" "$nlist" "${nlist}S" "${nlist}T" + + # Transform the symbol file into the correct name. + compile_command=`$echo "X$compile_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"` + finalize_command=`$echo "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"` + ;; + *) + $echo "$modename: unknown suffix for \`$dlsyms'" 1>&2 + exit $EXIT_FAILURE + ;; + esac + else + # We keep going just in case the user didn't refer to + # lt_preloaded_symbols. The linker will fail if global_symbol_pipe + # really was required. + + # Nullify the symbol file. + compile_command=`$echo "X$compile_command" | $Xsed -e "s% @SYMFILE@%%"` + finalize_command=`$echo "X$finalize_command" | $Xsed -e "s% @SYMFILE@%%"` + fi + + if test "$need_relink" = no || test "$build_libtool_libs" != yes; then + # Replace the output file specification. + compile_command=`$echo "X$compile_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'` + link_command="$compile_command$compile_rpath" + + # We have no uninstalled library dependencies, so finalize right now. + $show "$link_command" + $run eval "$link_command" + status=$? + + # Delete the generated files. + if test -n "$dlsyms"; then + $show "$rm $output_objdir/${outputname}S.${objext}" + $run $rm "$output_objdir/${outputname}S.${objext}" + fi + + exit $status + fi + + if test -n "$shlibpath_var"; then + # We should set the shlibpath_var + rpath= + for dir in $temp_rpath; do + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) + # Absolute path. + rpath="$rpath$dir:" + ;; + *) + # Relative path: add a thisdir entry. + rpath="$rpath\$thisdir/$dir:" + ;; + esac + done + temp_rpath="$rpath" + fi + + if test -n "$compile_shlibpath$finalize_shlibpath"; then + compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" + fi + if test -n "$finalize_shlibpath"; then + finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" + fi + + compile_var= + finalize_var= + if test -n "$runpath_var"; then + if test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + rpath="$rpath$dir:" + done + compile_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + if test -n "$finalize_perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $finalize_perm_rpath; do + rpath="$rpath$dir:" + done + finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + fi + + if test "$no_install" = yes; then + # We don't need to create a wrapper script. + link_command="$compile_var$compile_command$compile_rpath" + # Replace the output file specification. + link_command=`$echo "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'` + # Delete the old output file. + $run $rm $output + # Link the executable and exit + $show "$link_command" + $run eval "$link_command" || exit $? + exit $EXIT_SUCCESS + fi + + if test "$hardcode_action" = relink; then + # Fast installation is not supported + link_command="$compile_var$compile_command$compile_rpath" + relink_command="$finalize_var$finalize_command$finalize_rpath" + + $echo "$modename: warning: this platform does not like uninstalled shared libraries" 1>&2 + $echo "$modename: \`$output' will be relinked during installation" 1>&2 + else + if test "$fast_install" != no; then + link_command="$finalize_var$compile_command$finalize_rpath" + if test "$fast_install" = yes; then + relink_command=`$echo "X$compile_var$compile_command$compile_rpath" | $Xsed -e 's%@OUTPUT@%\$progdir/\$file%g'` + else + # fast_install is set to needless + relink_command= + fi + else + link_command="$compile_var$compile_command$compile_rpath" + relink_command="$finalize_var$finalize_command$finalize_rpath" + fi + fi + + # Replace the output file specification. + link_command=`$echo "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` + + # Delete the old output files. + $run $rm $output $output_objdir/$outputname $output_objdir/lt-$outputname + + $show "$link_command" + $run eval "$link_command" || exit $? + + # Now create the wrapper script. + $show "creating $output" + + # Quote the relink command for shipping. + if test -n "$relink_command"; then + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" + else + var_value=`$echo "X$var_value" | $Xsed -e "$sed_quote_subst"` + relink_command="$var=\"$var_value\"; export $var; $relink_command" + fi + done + relink_command="(cd `pwd`; $relink_command)" + relink_command=`$echo "X$relink_command" | $Xsed -e "$sed_quote_subst"` + fi + + # Quote $echo for shipping. + if test "X$echo" = "X$SHELL $progpath --fallback-echo"; then + case $progpath in + [\\/]* | [A-Za-z]:[\\/]*) qecho="$SHELL $progpath --fallback-echo";; + *) qecho="$SHELL `pwd`/$progpath --fallback-echo";; + esac + qecho=`$echo "X$qecho" | $Xsed -e "$sed_quote_subst"` + else + qecho=`$echo "X$echo" | $Xsed -e "$sed_quote_subst"` + fi + + # Only actually do things if our run command is non-null. + if test -z "$run"; then + # win32 will think the script is a binary if it has + # a .exe suffix, so we strip it off here. + case $output in + *.exe) output=`$echo $output|${SED} 's,.exe$,,'` ;; + esac + # test for cygwin because mv fails w/o .exe extensions + case $host in + *cygwin*) + exeext=.exe + outputname=`$echo $outputname|${SED} 's,.exe$,,'` ;; + *) exeext= ;; + esac + case $host in + *cygwin* | *mingw* ) + cwrappersource=`$echo ${objdir}/lt-${output}.c` + cwrapper=`$echo ${output}.exe` + $rm $cwrappersource $cwrapper + trap "$rm $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 + + cat > $cwrappersource <> $cwrappersource<<"EOF" +#include +#include +#include +#include +#include +#include + +#if defined(PATH_MAX) +# define LT_PATHMAX PATH_MAX +#elif defined(MAXPATHLEN) +# define LT_PATHMAX MAXPATHLEN +#else +# define LT_PATHMAX 1024 +#endif + +#ifndef DIR_SEPARATOR +#define DIR_SEPARATOR '/' +#endif + +#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \ + defined (__OS2__) +#define HAVE_DOS_BASED_FILE_SYSTEM +#ifndef DIR_SEPARATOR_2 +#define DIR_SEPARATOR_2 '\\' +#endif +#endif + +#ifndef DIR_SEPARATOR_2 +# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) +#else /* DIR_SEPARATOR_2 */ +# define IS_DIR_SEPARATOR(ch) \ + (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) +#endif /* DIR_SEPARATOR_2 */ + +#define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) +#define XFREE(stale) do { \ + if (stale) { free ((void *) stale); stale = 0; } \ +} while (0) + +const char *program_name = NULL; + +void * xmalloc (size_t num); +char * xstrdup (const char *string); +char * basename (const char *name); +char * fnqualify(const char *path); +char * strendzap(char *str, const char *pat); +void lt_fatal (const char *message, ...); + +int +main (int argc, char *argv[]) +{ + char **newargz; + int i; + + program_name = (char *) xstrdup ((char *) basename (argv[0])); + newargz = XMALLOC(char *, argc+2); +EOF + + cat >> $cwrappersource <> $cwrappersource <<"EOF" + newargz[1] = fnqualify(argv[0]); + /* we know the script has the same name, without the .exe */ + /* so make sure newargz[1] doesn't end in .exe */ + strendzap(newargz[1],".exe"); + for (i = 1; i < argc; i++) + newargz[i+1] = xstrdup(argv[i]); + newargz[argc+1] = NULL; +EOF + + cat >> $cwrappersource <> $cwrappersource <<"EOF" +} + +void * +xmalloc (size_t num) +{ + void * p = (void *) malloc (num); + if (!p) + lt_fatal ("Memory exhausted"); + + return p; +} + +char * +xstrdup (const char *string) +{ + return string ? strcpy ((char *) xmalloc (strlen (string) + 1), string) : NULL +; +} + +char * +basename (const char *name) +{ + const char *base; + +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + /* Skip over the disk name in MSDOS pathnames. */ + if (isalpha (name[0]) && name[1] == ':') + name += 2; +#endif + + for (base = name; *name; name++) + if (IS_DIR_SEPARATOR (*name)) + base = name + 1; + return (char *) base; +} + +char * +fnqualify(const char *path) +{ + size_t size; + char *p; + char tmp[LT_PATHMAX + 1]; + + assert(path != NULL); + + /* Is it qualified already? */ +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + if (isalpha (path[0]) && path[1] == ':') + return xstrdup (path); +#endif + if (IS_DIR_SEPARATOR (path[0])) + return xstrdup (path); + + /* prepend the current directory */ + /* doesn't handle '~' */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal ("getcwd failed"); + size = strlen(tmp) + 1 + strlen(path) + 1; /* +2 for '/' and '\0' */ + p = XMALLOC(char, size); + sprintf(p, "%s%c%s", tmp, DIR_SEPARATOR, path); + return p; +} + +char * +strendzap(char *str, const char *pat) +{ + size_t len, patlen; + + assert(str != NULL); + assert(pat != NULL); + + len = strlen(str); + patlen = strlen(pat); + + if (patlen <= len) + { + str += len - patlen; + if (strcmp(str, pat) == 0) + *str = '\0'; + } + return str; +} + +static void +lt_error_core (int exit_status, const char * mode, + const char * message, va_list ap) +{ + fprintf (stderr, "%s: %s: ", program_name, mode); + vfprintf (stderr, message, ap); + fprintf (stderr, ".\n"); + + if (exit_status >= 0) + exit (exit_status); +} + +void +lt_fatal (const char *message, ...) +{ + va_list ap; + va_start (ap, message); + lt_error_core (EXIT_FAILURE, "FATAL", message, ap); + va_end (ap); +} +EOF + # we should really use a build-platform specific compiler + # here, but OTOH, the wrappers (shell script and this C one) + # are only useful if you want to execute the "real" binary. + # Since the "real" binary is built for $host, then this + # wrapper might as well be built for $host, too. + $run $LTCC -s -o $cwrapper $cwrappersource + ;; + esac + $rm $output + trap "$rm $output; exit $EXIT_FAILURE" 1 2 15 + + $echo > $output "\ +#! $SHELL + +# $output - temporary wrapper script for $objdir/$outputname +# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP +# +# The $output program cannot be directly executed until all the libtool +# libraries that it depends on are installed. +# +# This wrapper script should never be moved out of the build directory. +# If it is, it will not operate correctly. + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +Xsed='${SED} -e 1s/^X//' +sed_quote_subst='$sed_quote_subst' + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +relink_command=\"$relink_command\" + +# This environment variable determines our operation mode. +if test \"\$libtool_install_magic\" = \"$magic\"; then + # install mode needs the following variable: + notinst_deplibs='$notinst_deplibs' +else + # When we are sourced in execute mode, \$file and \$echo are already set. + if test \"\$libtool_execute_magic\" != \"$magic\"; then + echo=\"$qecho\" + file=\"\$0\" + # Make sure echo works. + if test \"X\$1\" = X--no-reexec; then + # Discard the --no-reexec flag, and continue. + shift + elif test \"X\`(\$echo '\t') 2>/dev/null\`\" = 'X\t'; then + # Yippee, \$echo works! + : + else + # Restart under the correct shell, and then maybe \$echo will work. + exec $SHELL \"\$0\" --no-reexec \${1+\"\$@\"} + fi + fi\ +" + $echo >> $output "\ + + # Find the directory that this script lives in. + thisdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*$%%'\` + test \"x\$thisdir\" = \"x\$file\" && thisdir=. + + # Follow symbolic links until we get to the real thisdir. + file=\`ls -ld \"\$file\" | ${SED} -n 's/.*-> //p'\` + while test -n \"\$file\"; do + destdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*\$%%'\` + + # If there was a directory component, then change thisdir. + if test \"x\$destdir\" != \"x\$file\"; then + case \"\$destdir\" in + [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; + *) thisdir=\"\$thisdir/\$destdir\" ;; + esac + fi + + file=\`\$echo \"X\$file\" | \$Xsed -e 's%^.*/%%'\` + file=\`ls -ld \"\$thisdir/\$file\" | ${SED} -n 's/.*-> //p'\` + done + + # Try to get the absolute directory name. + absdir=\`cd \"\$thisdir\" && pwd\` + test -n \"\$absdir\" && thisdir=\"\$absdir\" +" + + if test "$fast_install" = yes; then + $echo >> $output "\ + program=lt-'$outputname'$exeext + progdir=\"\$thisdir/$objdir\" + + if test ! -f \"\$progdir/\$program\" || \\ + { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\ + test \"X\$file\" != \"X\$progdir/\$program\"; }; then + + file=\"\$\$-\$program\" + + if test ! -d \"\$progdir\"; then + $mkdir \"\$progdir\" + else + $rm \"\$progdir/\$file\" + fi" + + $echo >> $output "\ + + # relink executable if necessary + if test -n \"\$relink_command\"; then + if relink_command_output=\`eval \$relink_command 2>&1\`; then : + else + $echo \"\$relink_command_output\" >&2 + $rm \"\$progdir/\$file\" + exit $EXIT_FAILURE + fi + fi + + $mv \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || + { $rm \"\$progdir/\$program\"; + $mv \"\$progdir/\$file\" \"\$progdir/\$program\"; } + $rm \"\$progdir/\$file\" + fi" + else + $echo >> $output "\ + program='$outputname' + progdir=\"\$thisdir/$objdir\" +" + fi + + $echo >> $output "\ + + if test -f \"\$progdir/\$program\"; then" + + # Export our shlibpath_var if we have one. + if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then + $echo >> $output "\ + # Add our own library path to $shlibpath_var + $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" + + # Some systems cannot cope with colon-terminated $shlibpath_var + # The second colon is a workaround for a bug in BeOS R4 sed + $shlibpath_var=\`\$echo \"X\$$shlibpath_var\" | \$Xsed -e 's/::*\$//'\` + + export $shlibpath_var +" + fi + + # fixup the dll searchpath if we need to. + if test -n "$dllsearchpath"; then + $echo >> $output "\ + # Add the dll search path components to the executable PATH + PATH=$dllsearchpath:\$PATH +" + fi + + $echo >> $output "\ + if test \"\$libtool_execute_magic\" != \"$magic\"; then + # Run the actual program with our arguments. +" + case $host in + # Backslashes separate directories on plain windows + *-*-mingw | *-*-os2*) + $echo >> $output "\ + exec \$progdir\\\\\$program \${1+\"\$@\"} +" + ;; + + *) + $echo >> $output "\ + exec \$progdir/\$program \${1+\"\$@\"} +" + ;; + esac + $echo >> $output "\ + \$echo \"\$0: cannot exec \$program \${1+\"\$@\"}\" + exit $EXIT_FAILURE + fi + else + # The program doesn't exist. + \$echo \"\$0: error: \$progdir/\$program does not exist\" 1>&2 + \$echo \"This script is just a wrapper for \$program.\" 1>&2 + $echo \"See the $PACKAGE documentation for more information.\" 1>&2 + exit $EXIT_FAILURE + fi +fi\ +" + chmod +x $output + fi + exit $EXIT_SUCCESS + ;; + esac + + # See if we need to build an old-fashioned archive. + for oldlib in $oldlibs; do + + if test "$build_libtool_libs" = convenience; then + oldobjs="$libobjs_save" + addlibs="$convenience" + build_libtool_libs=no + else + if test "$build_libtool_libs" = module; then + oldobjs="$libobjs_save" + build_libtool_libs=no + else + oldobjs="$old_deplibs $non_pic_objects" + fi + addlibs="$old_convenience" + fi + + if test -n "$addlibs"; then + gentop="$output_objdir/${outputname}x" + generated="$generated $gentop" + + func_extract_archives $gentop $addlibs + oldobjs="$oldobjs $func_extract_archives_result" + fi + + # Do each command in the archive commands. + if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then + cmds=$old_archive_from_new_cmds + else + eval cmds=\"$old_archive_cmds\" + + if len=`expr "X$cmds" : ".*"` && + test "$len" -le "$max_cmd_len" || test "$max_cmd_len" -le -1; then + cmds=$old_archive_cmds + else + # the command line is too long to link in one step, link in parts + $echo "using piecewise archive linking..." + save_RANLIB=$RANLIB + RANLIB=: + objlist= + concat_cmds= + save_oldobjs=$oldobjs + # GNU ar 2.10+ was changed to match POSIX; thus no paths are + # encoded into archives. This makes 'ar r' malfunction in + # this piecewise linking case whenever conflicting object + # names appear in distinct ar calls; check, warn and compensate. + if (for obj in $save_oldobjs + do + $echo "X$obj" | $Xsed -e 's%^.*/%%' + done | sort | sort -uc >/dev/null 2>&1); then + : + else + $echo "$modename: warning: object name conflicts; overriding AR_FLAGS to 'cq'" 1>&2 + $echo "$modename: warning: to ensure that POSIX-compatible ar will work" 1>&2 + AR_FLAGS=cq + fi + # Is there a better way of finding the last object in the list? + for obj in $save_oldobjs + do + last_oldobj=$obj + done + for obj in $save_oldobjs + do + oldobjs="$objlist $obj" + objlist="$objlist $obj" + eval test_cmds=\"$old_archive_cmds\" + if len=`expr "X$test_cmds" : ".*"` && + test "$len" -le "$max_cmd_len"; then + : + else + # the above command should be used before it gets too long + oldobjs=$objlist + if test "$obj" = "$last_oldobj" ; then + RANLIB=$save_RANLIB + fi + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\" + objlist= + fi + done + RANLIB=$save_RANLIB + oldobjs=$objlist + if test "X$oldobjs" = "X" ; then + eval cmds=\"\$concat_cmds\" + else + eval cmds=\"\$concat_cmds~\$old_archive_cmds\" + fi + fi + fi + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + eval cmd=\"$cmd\" + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + done + + if test -n "$generated"; then + $show "${rm}r$generated" + $run ${rm}r$generated + fi + + # Now create the libtool archive. + case $output in + *.la) + old_library= + test "$build_old_libs" = yes && old_library="$libname.$libext" + $show "creating $output" + + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" + else + var_value=`$echo "X$var_value" | $Xsed -e "$sed_quote_subst"` + relink_command="$var=\"$var_value\"; export $var; $relink_command" + fi + done + # Quote the link command for shipping. + relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" + relink_command=`$echo "X$relink_command" | $Xsed -e "$sed_quote_subst"` + if test "$hardcode_automatic" = yes ; then + relink_command= + fi + + + # Only create the output if not a dry run. + if test -z "$run"; then + for installed in no yes; do + if test "$installed" = yes; then + if test -z "$install_libdir"; then + break + fi + output="$output_objdir/$outputname"i + # Replace all uninstalled libtool libraries with the installed ones + newdependency_libs= + for deplib in $dependency_libs; do + case $deplib in + *.la) + name=`$echo "X$deplib" | $Xsed -e 's%^.*/%%'` + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` + if test -z "$libdir"; then + $echo "$modename: \`$deplib' is not a valid libtool archive" 1>&2 + exit $EXIT_FAILURE + fi + newdependency_libs="$newdependency_libs $libdir/$name" + ;; + *) newdependency_libs="$newdependency_libs $deplib" ;; + esac + done + dependency_libs="$newdependency_libs" + newdlfiles= + for lib in $dlfiles; do + name=`$echo "X$lib" | $Xsed -e 's%^.*/%%'` + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + if test -z "$libdir"; then + $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 + exit $EXIT_FAILURE + fi + newdlfiles="$newdlfiles $libdir/$name" + done + dlfiles="$newdlfiles" + newdlprefiles= + for lib in $dlprefiles; do + name=`$echo "X$lib" | $Xsed -e 's%^.*/%%'` + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + if test -z "$libdir"; then + $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 + exit $EXIT_FAILURE + fi + newdlprefiles="$newdlprefiles $libdir/$name" + done + dlprefiles="$newdlprefiles" + else + newdlfiles= + for lib in $dlfiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; + *) abs=`pwd`"/$lib" ;; + esac + newdlfiles="$newdlfiles $abs" + done + dlfiles="$newdlfiles" + newdlprefiles= + for lib in $dlprefiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; + *) abs=`pwd`"/$lib" ;; + esac + newdlprefiles="$newdlprefiles $abs" + done + dlprefiles="$newdlprefiles" + fi + $rm $output + # place dlname in correct position for cygwin + tdlname=$dlname + case $host,$output,$installed,$module,$dlname in + *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll) tdlname=../bin/$dlname ;; + esac + $echo > $output "\ +# $outputname - a libtool library file +# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='$tdlname' + +# Names of this library. +library_names='$library_names' + +# The name of the static archive. +old_library='$old_library' + +# Libraries that this one depends upon. +dependency_libs='$dependency_libs' + +# Version information for $libname. +current=$current +age=$age +revision=$revision + +# Is this an already installed library? +installed=$installed + +# Should we warn about portability when linking against -modules? +shouldnotlink=$module + +# Files to dlopen/dlpreopen +dlopen='$dlfiles' +dlpreopen='$dlprefiles' + +# Directory that this library needs to be installed in: +libdir='$install_libdir'" + if test "$installed" = no && test "$need_relink" = yes; then + $echo >> $output "\ +relink_command=\"$relink_command\"" + fi + done + fi + + # Do a symbolic link so that the libtool archive can be found in + # LD_LIBRARY_PATH before the program is installed. + $show "(cd $output_objdir && $rm $outputname && $LN_S ../$outputname $outputname)" + $run eval '(cd $output_objdir && $rm $outputname && $LN_S ../$outputname $outputname)' || exit $? + ;; + esac + exit $EXIT_SUCCESS + ;; + + # libtool install mode + install) + modename="$modename: install" + + # There may be an optional sh(1) argument at the beginning of + # install_prog (especially on Windows NT). + if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh || + # Allow the use of GNU shtool's install command. + $echo "X$nonopt" | $Xsed | grep shtool > /dev/null; then + # Aesthetically quote it. + arg=`$echo "X$nonopt" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*) + arg="\"$arg\"" + ;; + esac + install_prog="$arg " + arg="$1" + shift + else + install_prog= + arg="$nonopt" + fi + + # The real first argument should be the name of the installation program. + # Aesthetically quote it. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*) + arg="\"$arg\"" + ;; + esac + install_prog="$install_prog$arg" + + # We need to accept at least all the BSD install flags. + dest= + files= + opts= + prev= + install_type= + isdir=no + stripme= + for arg + do + if test -n "$dest"; then + files="$files $dest" + dest="$arg" + continue + fi + + case $arg in + -d) isdir=yes ;; + -f) prev="-f" ;; + -g) prev="-g" ;; + -m) prev="-m" ;; + -o) prev="-o" ;; + -s) + stripme=" -s" + continue + ;; + -*) ;; + + *) + # If the previous option needed an argument, then skip it. + if test -n "$prev"; then + prev= + else + dest="$arg" + continue + fi + ;; + esac + + # Aesthetically quote the argument. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*) + arg="\"$arg\"" + ;; + esac + install_prog="$install_prog $arg" + done + + if test -z "$install_prog"; then + $echo "$modename: you must specify an install program" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + if test -n "$prev"; then + $echo "$modename: the \`$prev' option requires an argument" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + if test -z "$files"; then + if test -z "$dest"; then + $echo "$modename: no file or destination specified" 1>&2 + else + $echo "$modename: you must specify a destination" 1>&2 + fi + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + # Strip any trailing slash from the destination. + dest=`$echo "X$dest" | $Xsed -e 's%/$%%'` + + # Check to see that the destination is a directory. + test -d "$dest" && isdir=yes + if test "$isdir" = yes; then + destdir="$dest" + destname= + else + destdir=`$echo "X$dest" | $Xsed -e 's%/[^/]*$%%'` + test "X$destdir" = "X$dest" && destdir=. + destname=`$echo "X$dest" | $Xsed -e 's%^.*/%%'` + + # Not a directory, so check to see that there is only one file specified. + set dummy $files + if test "$#" -gt 2; then + $echo "$modename: \`$dest' is not a directory" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + fi + case $destdir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + for file in $files; do + case $file in + *.lo) ;; + *) + $echo "$modename: \`$destdir' must be an absolute directory name" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + ;; + esac + done + ;; + esac + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic="$magic" + + staticlibs= + future_libdirs= + current_libdirs= + for file in $files; do + + # Do each installation. + case $file in + *.$libext) + # Do the static libraries later. + staticlibs="$staticlibs $file" + ;; + + *.la) + # Check to see that this really is a libtool archive. + if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : + else + $echo "$modename: \`$file' is not a valid libtool archive" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + library_names= + old_library= + relink_command= + # If there is no directory component, then add one. + case $file in + */* | *\\*) . $file ;; + *) . ./$file ;; + esac + + # Add the libdir to current_libdirs if it is the destination. + if test "X$destdir" = "X$libdir"; then + case "$current_libdirs " in + *" $libdir "*) ;; + *) current_libdirs="$current_libdirs $libdir" ;; + esac + else + # Note the libdir as a future libdir. + case "$future_libdirs " in + *" $libdir "*) ;; + *) future_libdirs="$future_libdirs $libdir" ;; + esac + fi + + dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'`/ + test "X$dir" = "X$file/" && dir= + dir="$dir$objdir" + + if test -n "$relink_command"; then + # Determine the prefix the user has applied to our future dir. + inst_prefix_dir=`$echo "$destdir" | $SED "s%$libdir\$%%"` + + # Don't allow the user to place us outside of our expected + # location b/c this prevents finding dependent libraries that + # are installed to the same prefix. + # At present, this check doesn't affect windows .dll's that + # are installed into $libdir/../bin (currently, that works fine) + # but it's something to keep an eye on. + if test "$inst_prefix_dir" = "$destdir"; then + $echo "$modename: error: cannot install \`$file' to a directory not ending in $libdir" 1>&2 + exit $EXIT_FAILURE + fi + + if test -n "$inst_prefix_dir"; then + # Stick the inst_prefix_dir data into the link command. + relink_command=`$echo "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` + else + relink_command=`$echo "$relink_command" | $SED "s%@inst_prefix_dir@%%"` + fi + + $echo "$modename: warning: relinking \`$file'" 1>&2 + $show "$relink_command" + if $run eval "$relink_command"; then : + else + $echo "$modename: error: relink \`$file' with the above command before installing it" 1>&2 + exit $EXIT_FAILURE + fi + fi + + # See the names of the shared library. + set dummy $library_names + if test -n "$2"; then + realname="$2" + shift + shift + + srcname="$realname" + test -n "$relink_command" && srcname="$realname"T + + # Install the shared library and build the symlinks. + $show "$install_prog $dir/$srcname $destdir/$realname" + $run eval "$install_prog $dir/$srcname $destdir/$realname" || exit $? + if test -n "$stripme" && test -n "$striplib"; then + $show "$striplib $destdir/$realname" + $run eval "$striplib $destdir/$realname" || exit $? + fi + + if test "$#" -gt 0; then + # Delete the old symlinks, and create new ones. + for linkname + do + if test "$linkname" != "$realname"; then + $show "(cd $destdir && $rm $linkname && $LN_S $realname $linkname)" + $run eval "(cd $destdir && $rm $linkname && $LN_S $realname $linkname)" + fi + done + fi + + # Do each command in the postinstall commands. + lib="$destdir/$realname" + cmds=$postinstall_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + fi + + # Install the pseudo-library for information purposes. + name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + instname="$dir/$name"i + $show "$install_prog $instname $destdir/$name" + $run eval "$install_prog $instname $destdir/$name" || exit $? + + # Maybe install the static library, too. + test -n "$old_library" && staticlibs="$staticlibs $dir/$old_library" + ;; + + *.lo) + # Install (i.e. copy) a libtool object. + + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile="$destdir/$destname" + else + destfile=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + destfile="$destdir/$destfile" + fi + + # Deduce the name of the destination old-style object file. + case $destfile in + *.lo) + staticdest=`$echo "X$destfile" | $Xsed -e "$lo2o"` + ;; + *.$objext) + staticdest="$destfile" + destfile= + ;; + *) + $echo "$modename: cannot copy a libtool object to \`$destfile'" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + ;; + esac + + # Install the libtool object if requested. + if test -n "$destfile"; then + $show "$install_prog $file $destfile" + $run eval "$install_prog $file $destfile" || exit $? + fi + + # Install the old object if enabled. + if test "$build_old_libs" = yes; then + # Deduce the name of the old-style object file. + staticobj=`$echo "X$file" | $Xsed -e "$lo2o"` + + $show "$install_prog $staticobj $staticdest" + $run eval "$install_prog \$staticobj \$staticdest" || exit $? + fi + exit $EXIT_SUCCESS + ;; + + *) + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile="$destdir/$destname" + else + destfile=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + destfile="$destdir/$destfile" + fi + + # If the file is missing, and there is a .exe on the end, strip it + # because it is most likely a libtool script we actually want to + # install + stripped_ext="" + case $file in + *.exe) + if test ! -f "$file"; then + file=`$echo $file|${SED} 's,.exe$,,'` + stripped_ext=".exe" + fi + ;; + esac + + # Do a test to see if this is really a libtool program. + case $host in + *cygwin*|*mingw*) + wrapper=`$echo $file | ${SED} -e 's,.exe$,,'` + ;; + *) + wrapper=$file + ;; + esac + if (${SED} -e '4q' $wrapper | grep "^# Generated by .*$PACKAGE")>/dev/null 2>&1; then + notinst_deplibs= + relink_command= + + # To insure that "foo" is sourced, and not "foo.exe", + # finese the cygwin/MSYS system by explicitly sourcing "foo." + # which disallows the automatic-append-.exe behavior. + case $build in + *cygwin* | *mingw*) wrapperdot=${wrapper}. ;; + *) wrapperdot=${wrapper} ;; + esac + # If there is no directory component, then add one. + case $file in + */* | *\\*) . ${wrapperdot} ;; + *) . ./${wrapperdot} ;; + esac + + # Check the variables that should have been set. + if test -z "$notinst_deplibs"; then + $echo "$modename: invalid libtool wrapper script \`$wrapper'" 1>&2 + exit $EXIT_FAILURE + fi + + finalize=yes + for lib in $notinst_deplibs; do + # Check to see that each library is installed. + libdir= + if test -f "$lib"; then + # If there is no directory component, then add one. + case $lib in + */* | *\\*) . $lib ;; + *) . ./$lib ;; + esac + fi + libfile="$libdir/"`$echo "X$lib" | $Xsed -e 's%^.*/%%g'` ### testsuite: skip nested quoting test + if test -n "$libdir" && test ! -f "$libfile"; then + $echo "$modename: warning: \`$lib' has not been installed in \`$libdir'" 1>&2 + finalize=no + fi + done + + relink_command= + # To insure that "foo" is sourced, and not "foo.exe", + # finese the cygwin/MSYS system by explicitly sourcing "foo." + # which disallows the automatic-append-.exe behavior. + case $build in + *cygwin* | *mingw*) wrapperdot=${wrapper}. ;; + *) wrapperdot=${wrapper} ;; + esac + # If there is no directory component, then add one. + case $file in + */* | *\\*) . ${wrapperdot} ;; + *) . ./${wrapperdot} ;; + esac + + outputname= + if test "$fast_install" = no && test -n "$relink_command"; then + if test "$finalize" = yes && test -z "$run"; then + tmpdir="/tmp" + test -n "$TMPDIR" && tmpdir="$TMPDIR" + tmpdir="$tmpdir/libtool-$$" + save_umask=`umask` + umask 0077 + if $mkdir "$tmpdir"; then + umask $save_umask + else + umask $save_umask + $echo "$modename: error: cannot create temporary directory \`$tmpdir'" 1>&2 + continue + fi + file=`$echo "X$file$stripped_ext" | $Xsed -e 's%^.*/%%'` + outputname="$tmpdir/$file" + # Replace the output file specification. + relink_command=`$echo "X$relink_command" | $Xsed -e 's%@OUTPUT@%'"$outputname"'%g'` + + $show "$relink_command" + if $run eval "$relink_command"; then : + else + $echo "$modename: error: relink \`$file' with the above command before installing it" 1>&2 + ${rm}r "$tmpdir" + continue + fi + file="$outputname" + else + $echo "$modename: warning: cannot relink \`$file'" 1>&2 + fi + else + # Install the binary that we compiled earlier. + file=`$echo "X$file$stripped_ext" | $Xsed -e "s%\([^/]*\)$%$objdir/\1%"` + fi + fi + + # remove .exe since cygwin /usr/bin/install will append another + # one anyways + case $install_prog,$host in + */usr/bin/install*,*cygwin*) + case $file:$destfile in + *.exe:*.exe) + # this is ok + ;; + *.exe:*) + destfile=$destfile.exe + ;; + *:*.exe) + destfile=`$echo $destfile | ${SED} -e 's,.exe$,,'` + ;; + esac + ;; + esac + $show "$install_prog$stripme $file $destfile" + $run eval "$install_prog\$stripme \$file \$destfile" || exit $? + test -n "$outputname" && ${rm}r "$tmpdir" + ;; + esac + done + + for file in $staticlibs; do + name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + + # Set up the ranlib parameters. + oldlib="$destdir/$name" + + $show "$install_prog $file $oldlib" + $run eval "$install_prog \$file \$oldlib" || exit $? + + if test -n "$stripme" && test -n "$old_striplib"; then + $show "$old_striplib $oldlib" + $run eval "$old_striplib $oldlib" || exit $? + fi + + # Do each command in the postinstall commands. + cmds=$old_postinstall_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + done + + if test -n "$future_libdirs"; then + $echo "$modename: warning: remember to run \`$progname --finish$future_libdirs'" 1>&2 + fi + + if test -n "$current_libdirs"; then + # Maybe just do a dry run. + test -n "$run" && current_libdirs=" -n$current_libdirs" + exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs' + else + exit $EXIT_SUCCESS + fi + ;; + + # libtool finish mode + finish) + modename="$modename: finish" + libdirs="$nonopt" + admincmds= + + if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then + for dir + do + libdirs="$libdirs $dir" + done + + for libdir in $libdirs; do + if test -n "$finish_cmds"; then + # Do each command in the finish commands. + cmds=$finish_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || admincmds="$admincmds + $cmd" + done + IFS="$save_ifs" + fi + if test -n "$finish_eval"; then + # Do the single finish_eval. + eval cmds=\"$finish_eval\" + $run eval "$cmds" || admincmds="$admincmds + $cmds" + fi + done + fi + + # Exit here if they wanted silent mode. + test "$show" = : && exit $EXIT_SUCCESS + + $echo "----------------------------------------------------------------------" + $echo "Libraries have been installed in:" + for libdir in $libdirs; do + $echo " $libdir" + done + $echo + $echo "If you ever happen to want to link against installed libraries" + $echo "in a given directory, LIBDIR, you must either use libtool, and" + $echo "specify the full pathname of the library, or use the \`-LLIBDIR'" + $echo "flag during linking and do at least one of the following:" + if test -n "$shlibpath_var"; then + $echo " - add LIBDIR to the \`$shlibpath_var' environment variable" + $echo " during execution" + fi + if test -n "$runpath_var"; then + $echo " - add LIBDIR to the \`$runpath_var' environment variable" + $echo " during linking" + fi + if test -n "$hardcode_libdir_flag_spec"; then + libdir=LIBDIR + eval flag=\"$hardcode_libdir_flag_spec\" + + $echo " - use the \`$flag' linker flag" + fi + if test -n "$admincmds"; then + $echo " - have your system administrator run these commands:$admincmds" + fi + if test -f /etc/ld.so.conf; then + $echo " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'" + fi + $echo + $echo "See any operating system documentation about shared libraries for" + $echo "more information, such as the ld(1) and ld.so(8) manual pages." + $echo "----------------------------------------------------------------------" + exit $EXIT_SUCCESS + ;; + + # libtool execute mode + execute) + modename="$modename: execute" + + # The first argument is the command name. + cmd="$nonopt" + if test -z "$cmd"; then + $echo "$modename: you must specify a COMMAND" 1>&2 + $echo "$help" + exit $EXIT_FAILURE + fi + + # Handle -dlopen flags immediately. + for file in $execute_dlfiles; do + if test ! -f "$file"; then + $echo "$modename: \`$file' is not a file" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + dir= + case $file in + *.la) + # Check to see that this really is a libtool archive. + if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : + else + $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + # Read the libtool library. + dlname= + library_names= + + # If there is no directory component, then add one. + case $file in + */* | *\\*) . $file ;; + *) . ./$file ;; + esac + + # Skip this library if it cannot be dlopened. + if test -z "$dlname"; then + # Warn if it was a shared library. + test -n "$library_names" && $echo "$modename: warning: \`$file' was not linked with \`-export-dynamic'" + continue + fi + + dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` + test "X$dir" = "X$file" && dir=. + + if test -f "$dir/$objdir/$dlname"; then + dir="$dir/$objdir" + else + $echo "$modename: cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" 1>&2 + exit $EXIT_FAILURE + fi + ;; + + *.lo) + # Just add the directory containing the .lo file. + dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` + test "X$dir" = "X$file" && dir=. + ;; + + *) + $echo "$modename: warning \`-dlopen' is ignored for non-libtool libraries and objects" 1>&2 + continue + ;; + esac + + # Get the absolute pathname. + absdir=`cd "$dir" && pwd` + test -n "$absdir" && dir="$absdir" + + # Now add the directory to shlibpath_var. + if eval "test -z \"\$$shlibpath_var\""; then + eval "$shlibpath_var=\"\$dir\"" + else + eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" + fi + done + + # This variable tells wrapper scripts just to set shlibpath_var + # rather than running their programs. + libtool_execute_magic="$magic" + + # Check if any of the arguments is a wrapper script. + args= + for file + do + case $file in + -*) ;; + *) + # Do a test to see if this is really a libtool program. + if (${SED} -e '4q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + # If there is no directory component, then add one. + case $file in + */* | *\\*) . $file ;; + *) . ./$file ;; + esac + + # Transform arg to wrapped name. + file="$progdir/$program" + fi + ;; + esac + # Quote arguments (to preserve shell metacharacters). + file=`$echo "X$file" | $Xsed -e "$sed_quote_subst"` + args="$args \"$file\"" + done + + if test -z "$run"; then + if test -n "$shlibpath_var"; then + # Export the shlibpath_var. + eval "export $shlibpath_var" + fi + + # Restore saved environment variables + if test "${save_LC_ALL+set}" = set; then + LC_ALL="$save_LC_ALL"; export LC_ALL + fi + if test "${save_LANG+set}" = set; then + LANG="$save_LANG"; export LANG + fi + + # Now prepare to actually exec the command. + exec_cmd="\"\$cmd\"$args" + else + # Display what would be done. + if test -n "$shlibpath_var"; then + eval "\$echo \"\$shlibpath_var=\$$shlibpath_var\"" + $echo "export $shlibpath_var" + fi + eval \$echo \"\$cmd\"$args + exit $EXIT_SUCCESS + fi + ;; + + # libtool clean and uninstall mode + clean | uninstall) + modename="$modename: $mode" + rm="$nonopt" + files= + rmforce= + exit_status=0 + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic="$magic" + + for arg + do + case $arg in + -f) rm="$rm $arg"; rmforce=yes ;; + -*) rm="$rm $arg" ;; + *) files="$files $arg" ;; + esac + done + + if test -z "$rm"; then + $echo "$modename: you must specify an RM program" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + rmdirs= + + origobjdir="$objdir" + for file in $files; do + dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` + if test "X$dir" = "X$file"; then + dir=. + objdir="$origobjdir" + else + objdir="$dir/$origobjdir" + fi + name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + test "$mode" = uninstall && objdir="$dir" + + # Remember objdir for removal later, being careful to avoid duplicates + if test "$mode" = clean; then + case " $rmdirs " in + *" $objdir "*) ;; + *) rmdirs="$rmdirs $objdir" ;; + esac + fi + + # Don't error if the file doesn't exist and rm -f was used. + if (test -L "$file") >/dev/null 2>&1 \ + || (test -h "$file") >/dev/null 2>&1 \ + || test -f "$file"; then + : + elif test -d "$file"; then + exit_status=1 + continue + elif test "$rmforce" = yes; then + continue + fi + + rmfiles="$file" + + case $name in + *.la) + # Possibly a libtool archive, so verify it. + if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + . $dir/$name + + # Delete the libtool libraries and symlinks. + for n in $library_names; do + rmfiles="$rmfiles $objdir/$n" + done + test -n "$old_library" && rmfiles="$rmfiles $objdir/$old_library" + test "$mode" = clean && rmfiles="$rmfiles $objdir/$name $objdir/${name}i" + + if test "$mode" = uninstall; then + if test -n "$library_names"; then + # Do each command in the postuninstall commands. + cmds=$postuninstall_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" + if test "$?" -ne 0 && test "$rmforce" != yes; then + exit_status=1 + fi + done + IFS="$save_ifs" + fi + + if test -n "$old_library"; then + # Do each command in the old_postuninstall commands. + cmds=$old_postuninstall_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" + if test "$?" -ne 0 && test "$rmforce" != yes; then + exit_status=1 + fi + done + IFS="$save_ifs" + fi + # FIXME: should reinstall the best remaining shared library. + fi + fi + ;; + + *.lo) + # Possibly a libtool object, so verify it. + if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + + # Read the .lo file + . $dir/$name + + # Add PIC object to the list of files to remove. + if test -n "$pic_object" \ + && test "$pic_object" != none; then + rmfiles="$rmfiles $dir/$pic_object" + fi + + # Add non-PIC object to the list of files to remove. + if test -n "$non_pic_object" \ + && test "$non_pic_object" != none; then + rmfiles="$rmfiles $dir/$non_pic_object" + fi + fi + ;; + + *) + if test "$mode" = clean ; then + noexename=$name + case $file in + *.exe) + file=`$echo $file|${SED} 's,.exe$,,'` + noexename=`$echo $name|${SED} 's,.exe$,,'` + # $file with .exe has already been added to rmfiles, + # add $file without .exe + rmfiles="$rmfiles $file" + ;; + esac + # Do a test to see if this is a libtool program. + if (${SED} -e '4q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + relink_command= + . $dir/$noexename + + # note $name still contains .exe if it was in $file originally + # as does the version of $file that was added into $rmfiles + rmfiles="$rmfiles $objdir/$name $objdir/${name}S.${objext}" + if test "$fast_install" = yes && test -n "$relink_command"; then + rmfiles="$rmfiles $objdir/lt-$name" + fi + if test "X$noexename" != "X$name" ; then + rmfiles="$rmfiles $objdir/lt-${noexename}.c" + fi + fi + fi + ;; + esac + $show "$rm $rmfiles" + $run $rm $rmfiles || exit_status=1 + done + objdir="$origobjdir" + + # Try to remove the ${objdir}s in the directories where we deleted files + for dir in $rmdirs; do + if test -d "$dir"; then + $show "rmdir $dir" + $run rmdir $dir >/dev/null 2>&1 + fi + done + + exit $exit_status + ;; + + "") + $echo "$modename: you must specify a MODE" 1>&2 + $echo "$generic_help" 1>&2 + exit $EXIT_FAILURE + ;; + esac + + if test -z "$exec_cmd"; then + $echo "$modename: invalid operation mode \`$mode'" 1>&2 + $echo "$generic_help" 1>&2 + exit $EXIT_FAILURE + fi +fi # test -z "$show_help" + +if test -n "$exec_cmd"; then + eval exec $exec_cmd + exit $EXIT_FAILURE +fi + +# We need to display help for each of the modes. +case $mode in +"") $echo \ +"Usage: $modename [OPTION]... [MODE-ARG]... + +Provide generalized library-building support services. + + --config show all configuration variables + --debug enable verbose shell tracing +-n, --dry-run display commands without modifying any files + --features display basic configuration information and exit + --finish same as \`--mode=finish' + --help display this help message and exit + --mode=MODE use operation mode MODE [default=inferred from MODE-ARGS] + --quiet same as \`--silent' + --silent don't print informational messages + --tag=TAG use configuration variables from tag TAG + --version print version information + +MODE must be one of the following: + + clean remove files from the build directory + compile compile a source file into a libtool object + execute automatically set library path, then run a program + finish complete the installation of libtool libraries + install install libraries or executables + link create a library or an executable + uninstall remove libraries from an installed directory + +MODE-ARGS vary depending on the MODE. Try \`$modename --help --mode=MODE' for +a more detailed description of MODE. + +Report bugs to ." + exit $EXIT_SUCCESS + ;; + +clean) + $echo \ +"Usage: $modename [OPTION]... --mode=clean RM [RM-OPTION]... FILE... + +Remove files from the build directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed +to RM. + +If FILE is a libtool library, object or program, all the files associated +with it are deleted. Otherwise, only FILE itself is deleted using RM." + ;; + +compile) + $echo \ +"Usage: $modename [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE + +Compile a source file into a libtool library object. + +This mode accepts the following additional options: + + -o OUTPUT-FILE set the output file name to OUTPUT-FILE + -prefer-pic try to building PIC objects only + -prefer-non-pic try to building non-PIC objects only + -static always build a \`.o' file suitable for static linking + +COMPILE-COMMAND is a command to be used in creating a \`standard' object file +from the given SOURCEFILE. + +The output file name is determined by removing the directory component from +SOURCEFILE, then substituting the C source code suffix \`.c' with the +library object suffix, \`.lo'." + ;; + +execute) + $echo \ +"Usage: $modename [OPTION]... --mode=execute COMMAND [ARGS]... + +Automatically set library path, then run a program. + +This mode accepts the following additional options: + + -dlopen FILE add the directory containing FILE to the library path + +This mode sets the library path environment variable according to \`-dlopen' +flags. + +If any of the ARGS are libtool executable wrappers, then they are translated +into their corresponding uninstalled binary, and any of their required library +directories are added to the library path. + +Then, COMMAND is executed, with ARGS as arguments." + ;; + +finish) + $echo \ +"Usage: $modename [OPTION]... --mode=finish [LIBDIR]... + +Complete the installation of libtool libraries. + +Each LIBDIR is a directory that contains libtool libraries. + +The commands that this mode executes may require superuser privileges. Use +the \`--dry-run' option if you just want to see what would be executed." + ;; + +install) + $echo \ +"Usage: $modename [OPTION]... --mode=install INSTALL-COMMAND... + +Install executables or libraries. + +INSTALL-COMMAND is the installation command. The first component should be +either the \`install' or \`cp' program. + +The rest of the components are interpreted as arguments to that command (only +BSD-compatible install options are recognized)." + ;; + +link) + $echo \ +"Usage: $modename [OPTION]... --mode=link LINK-COMMAND... + +Link object files or libraries together to form another library, or to +create an executable program. + +LINK-COMMAND is a command using the C compiler that you would use to create +a program from several object files. + +The following components of LINK-COMMAND are treated specially: + + -all-static do not do any dynamic linking at all + -avoid-version do not add a version suffix if possible + -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime + -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols + -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) + -export-symbols SYMFILE + try to export only the symbols listed in SYMFILE + -export-symbols-regex REGEX + try to export only the symbols matching REGEX + -LLIBDIR search LIBDIR for required installed libraries + -lNAME OUTPUT-FILE requires the installed library libNAME + -module build a library that can dlopened + -no-fast-install disable the fast-install mode + -no-install link a not-installable executable + -no-undefined declare that a library does not refer to external symbols + -o OUTPUT-FILE create OUTPUT-FILE from the specified objects + -objectlist FILE Use a list of object files found in FILE to specify objects + -precious-files-regex REGEX + don't remove output files matching REGEX + -release RELEASE specify package release information + -rpath LIBDIR the created library will eventually be installed in LIBDIR + -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries + -static do not do any dynamic linking of libtool libraries + -version-info CURRENT[:REVISION[:AGE]] + specify library version info [each variable defaults to 0] + +All other options (arguments beginning with \`-') are ignored. + +Every other argument is treated as a filename. Files ending in \`.la' are +treated as uninstalled libtool libraries, other files are standard or library +object files. + +If the OUTPUT-FILE ends in \`.la', then a libtool library is created, +only library objects (\`.lo' files) may be specified, and \`-rpath' is +required, except when creating a convenience library. + +If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created +using \`ar' and \`ranlib', or on Windows using \`lib'. + +If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file +is created, otherwise an executable program is created." + ;; + +uninstall) + $echo \ +"Usage: $modename [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... + +Remove libraries from an installation directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed +to RM. + +If FILE is a libtool library, all the files associated with it are deleted. +Otherwise, only FILE itself is deleted using RM." + ;; + +*) + $echo "$modename: invalid operation mode \`$mode'" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + ;; +esac + +$echo +$echo "Try \`$modename --help' for more information about other modes." + +exit $EXIT_SUCCESS + +# The TAGs below are defined such that we never get into a situation +# in which we disable both kinds of libraries. Given conflicting +# choices, we go for a static library, that is the most portable, +# since we can't tell whether shared libraries were disabled because +# the user asked for that or because the platform doesn't support +# them. This is particularly important on AIX, because we don't +# support having both static and shared libraries enabled at the same +# time on that platform, so we default to a shared-only configuration. +# If a disable-shared tag is given, we'll fallback to a static-only +# configuration. But we'll never go from static-only to shared-only. + +# ### BEGIN LIBTOOL TAG CONFIG: disable-shared +build_libtool_libs=no +build_old_libs=yes +# ### END LIBTOOL TAG CONFIG: disable-shared + +# ### BEGIN LIBTOOL TAG CONFIG: disable-static +build_old_libs=`case $build_libtool_libs in yes) $echo no;; *) $echo yes;; esac` +# ### END LIBTOOL TAG CONFIG: disable-static + +# Local Variables: +# mode:shell-script +# sh-indentation:2 +# End: diff --git a/makedist.sh b/makedist.sh new file mode 100644 index 0000000..5389c14 --- /dev/null +++ b/makedist.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +echo Creating autoconf scripts... +sh ./reconfig.sh + +echo Configuring... +./configure + +sh ./makedist2.sh + diff --git a/makedist2.sh.in b/makedist2.sh.in new file mode 100644 index 0000000..7c8dd46 --- /dev/null +++ b/makedist2.sh.in @@ -0,0 +1,27 @@ +#!/bin/sh + +# create distribution file +make dist + +# create tar archive and signature +tarArchive=@PACKAGE@-@VERSION@-@RELEASE@.tgz +mv @PACKAGE@-@VERSION@.tar.gz $tarArchive +# let the user know why they're being asked for a passpharse +echo "Signing tar archive - enter GPG password"; +gpg --detach-sign -a $tarArchive + +# create rpms +#cp $tarArchive /usr/src/packages/SOURCES +#echo "Building signed RPM files - enter GPG password"; +#rpmbuild -ba --sign @PACKAGE@.spec + +# move all distribution files to dist directory +mkdir dist +mv $tarArchive dist +mv $tarArchive.asc dist +#mv /usr/src/packages/SRPMS/@PACKAGE@-@VERSION@-@RELEASE@.src.rpm dist +#mv /usr/src/packages/RPMS/i586/@PACKAGE@-@VERSION@-@RELEASE@.i586.rpm dist + +# cleanup +#rm /usr/src/packages/SOURCES/$tarArchive + diff --git a/reconfig.sh b/reconfig.sh new file mode 100644 index 0000000..371f458 --- /dev/null +++ b/reconfig.sh @@ -0,0 +1,6 @@ +#!/bin/sh +aclocal +autoheader +automake -a +autoconf + diff --git a/subdirs b/subdirs new file mode 100644 index 0000000..6897267 --- /dev/null +++ b/subdirs @@ -0,0 +1,3 @@ +admin +autom4te.cache +encfs