From 4fc836f03222e9b1cdcf9c56e934db5c531019e6 Mon Sep 17 00:00:00 2001 From: Valient Gough Date: Mon, 7 Jan 2008 08:09:04 +0000 Subject: [PATCH] Import version 1.4.0 git-svn-id: http://encfs.googlecode.com/svn/trunk@2 db9cf616-1c43-0410-9cb8-a902689de0d6 --- AUTHORS | 8 + COPYING | 674 ++++ ChangeLog | 1190 +++++++ INSTALL | 23 + Makefile.am | 17 + Makefile.common | 2 + Makefile.dist | 8 + README | 132 + README-NLS | 71 + TRANSLATORS | 7 + acinclude.m4 | 3 + acx_pthread.m4 | 199 ++ configure.ac | 187 ++ dk2ChangeLog | 32 + encfs.spec.in | 207 ++ encfs/BlockFileIO.cpp | 431 +++ encfs/BlockFileIO.h | 69 + encfs/BlockNameIO.cpp | 213 ++ encfs/BlockNameIO.h | 65 + encfs/Cipher.cpp | 208 ++ encfs/Cipher.h | 151 + encfs/CipherFileIO.cpp | 424 +++ encfs/CipherFileIO.h | 85 + encfs/CipherKey.cpp | 30 + encfs/CipherKey.h | 36 + encfs/ConfigReader.cpp | 159 + encfs/ConfigReader.h | 63 + encfs/ConfigVar.cpp | 247 ++ encfs/ConfigVar.h | 80 + encfs/Context.cpp | 182 ++ encfs/Context.h | 106 + encfs/DirNode.cpp | 808 +++++ encfs/DirNode.h | 210 ++ encfs/FileIO.cpp | 38 + encfs/FileIO.h | 84 + encfs/FileNode.cpp | 296 ++ encfs/FileNode.h | 104 + encfs/FileUtils.cpp | 1438 +++++++++ encfs/FileUtils.h | 161 + encfs/Interface.cpp | 226 ++ encfs/Interface.h | 84 + encfs/MACFileIO.cpp | 270 ++ encfs/MACFileIO.h | 69 + encfs/Makefile.am | 138 + encfs/MemoryPool.cpp | 151 + encfs/MemoryPool.h | 54 + encfs/Mutex.h | 62 + encfs/NameIO.cpp | 348 ++ encfs/NameIO.h | 136 + encfs/NullCipher.cpp | 177 + encfs/NullCipher.h | 80 + encfs/NullNameIO.cpp | 86 + encfs/NullNameIO.h | 49 + encfs/Range.h | 110 + encfs/RawFileIO.cpp | 322 ++ encfs/RawFileIO.h | 61 + encfs/SSL_Cipher.cpp | 816 +++++ encfs/SSL_Cipher.h | 141 + encfs/StreamNameIO.cpp | 205 ++ encfs/StreamNameIO.h | 57 + encfs/base64.cpp | 164 + encfs/base64.h | 55 + encfs/docs/Makefile.am | 4 + encfs/docs/en/Makefile.am | 4 + encfs/encfs.cpp | 765 +++++ encfs/encfs.h | 104 + encfs/encfs.pod | 464 +++ encfs/encfsctl.cpp | 708 ++++ encfs/encfsctl.pod | 99 + encfs/encfssh | 67 + encfs/i18n.h | 41 + encfs/main.cpp | 745 +++++ encfs/openssl.cpp | 109 + encfs/openssl.h | 29 + encfs/readpassphrase.cpp | 195 ++ encfs/readpassphrase.h | 52 + encfs/test.cpp | 543 ++++ encfs/testextpass | 45 + ltmain.sh | 6402 +++++++++++++++++++++++++++++++++++++ makedist.sh | 10 + makedist2.sh.in | 27 + reconfig.sh | 6 + subdirs | 3 + 83 files changed, 22731 insertions(+) create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 Makefile.common create mode 100644 Makefile.dist create mode 100644 README create mode 100644 README-NLS create mode 100644 TRANSLATORS create mode 100644 acinclude.m4 create mode 100644 acx_pthread.m4 create mode 100644 configure.ac create mode 100644 dk2ChangeLog create mode 100644 encfs.spec.in create mode 100644 encfs/BlockFileIO.cpp create mode 100644 encfs/BlockFileIO.h create mode 100644 encfs/BlockNameIO.cpp create mode 100644 encfs/BlockNameIO.h create mode 100644 encfs/Cipher.cpp create mode 100644 encfs/Cipher.h create mode 100644 encfs/CipherFileIO.cpp create mode 100644 encfs/CipherFileIO.h create mode 100644 encfs/CipherKey.cpp create mode 100644 encfs/CipherKey.h create mode 100644 encfs/ConfigReader.cpp create mode 100644 encfs/ConfigReader.h create mode 100644 encfs/ConfigVar.cpp create mode 100644 encfs/ConfigVar.h create mode 100644 encfs/Context.cpp create mode 100644 encfs/Context.h create mode 100644 encfs/DirNode.cpp create mode 100644 encfs/DirNode.h create mode 100644 encfs/FileIO.cpp create mode 100644 encfs/FileIO.h create mode 100644 encfs/FileNode.cpp create mode 100644 encfs/FileNode.h create mode 100644 encfs/FileUtils.cpp create mode 100644 encfs/FileUtils.h create mode 100644 encfs/Interface.cpp create mode 100644 encfs/Interface.h create mode 100644 encfs/MACFileIO.cpp create mode 100644 encfs/MACFileIO.h create mode 100644 encfs/Makefile.am create mode 100644 encfs/MemoryPool.cpp create mode 100644 encfs/MemoryPool.h create mode 100644 encfs/Mutex.h create mode 100644 encfs/NameIO.cpp create mode 100644 encfs/NameIO.h create mode 100644 encfs/NullCipher.cpp create mode 100644 encfs/NullCipher.h create mode 100644 encfs/NullNameIO.cpp create mode 100644 encfs/NullNameIO.h create mode 100644 encfs/Range.h create mode 100644 encfs/RawFileIO.cpp create mode 100644 encfs/RawFileIO.h create mode 100644 encfs/SSL_Cipher.cpp create mode 100644 encfs/SSL_Cipher.h create mode 100644 encfs/StreamNameIO.cpp create mode 100644 encfs/StreamNameIO.h create mode 100644 encfs/base64.cpp create mode 100644 encfs/base64.h create mode 100644 encfs/docs/Makefile.am create mode 100644 encfs/docs/en/Makefile.am create mode 100644 encfs/encfs.cpp create mode 100644 encfs/encfs.h create mode 100644 encfs/encfs.pod create mode 100644 encfs/encfsctl.cpp create mode 100644 encfs/encfsctl.pod create mode 100644 encfs/encfssh create mode 100644 encfs/i18n.h create mode 100644 encfs/main.cpp create mode 100644 encfs/openssl.cpp create mode 100644 encfs/openssl.h create mode 100644 encfs/readpassphrase.cpp create mode 100644 encfs/readpassphrase.h create mode 100644 encfs/test.cpp create mode 100644 encfs/testextpass create mode 100644 ltmain.sh create mode 100644 makedist.sh create mode 100644 makedist2.sh.in create mode 100644 reconfig.sh create mode 100644 subdirs 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